Commit | Line | Data |
---|---|---|
5ad36c5f | 1 | /* |
938fa349 | 2 | * Copyright (C) 2011 Google, Inc. |
5ad36c5f EG |
3 | * |
4 | * Author: | |
938fa349 | 5 | * Colin Cross <ccross@android.com> |
5ad36c5f | 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> | |
5ad36c5f EG |
21 | #include <linux/interrupt.h> |
22 | #include <linux/irq.h> | |
23 | #include <linux/io.h> | |
0d4f7479 | 24 | #include <linux/of.h> |
5ad36c5f EG |
25 | |
26 | #include <asm/hardware/gic.h> | |
27 | ||
5ad36c5f | 28 | #include "board.h" |
2be39c07 | 29 | #include "iomap.h" |
5ad36c5f | 30 | |
d1d8c666 CC |
31 | #define ICTLR_CPU_IEP_VFIQ 0x08 |
32 | #define ICTLR_CPU_IEP_FIR 0x14 | |
33 | #define ICTLR_CPU_IEP_FIR_SET 0x18 | |
34 | #define ICTLR_CPU_IEP_FIR_CLR 0x1c | |
35 | ||
36 | #define ICTLR_CPU_IER 0x20 | |
37 | #define ICTLR_CPU_IER_SET 0x24 | |
38 | #define ICTLR_CPU_IER_CLR 0x28 | |
39 | #define ICTLR_CPU_IEP_CLASS 0x2C | |
40 | ||
41 | #define ICTLR_COP_IER 0x30 | |
42 | #define ICTLR_COP_IER_SET 0x34 | |
43 | #define ICTLR_COP_IER_CLR 0x38 | |
44 | #define ICTLR_COP_IEP_CLASS 0x3c | |
45 | ||
d1d8c666 CC |
46 | #define FIRST_LEGACY_IRQ 32 |
47 | ||
caa4868e PDS |
48 | static int num_ictlrs; |
49 | ||
d1d8c666 CC |
50 | static void __iomem *ictlr_reg_base[] = { |
51 | IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE), | |
52 | IO_ADDRESS(TEGRA_SECONDARY_ICTLR_BASE), | |
53 | IO_ADDRESS(TEGRA_TERTIARY_ICTLR_BASE), | |
54 | IO_ADDRESS(TEGRA_QUATERNARY_ICTLR_BASE), | |
caa4868e | 55 | IO_ADDRESS(TEGRA_QUINARY_ICTLR_BASE), |
d1d8c666 CC |
56 | }; |
57 | ||
58 | static inline void tegra_irq_write_mask(unsigned int irq, unsigned long reg) | |
59 | { | |
60 | void __iomem *base; | |
61 | u32 mask; | |
62 | ||
63 | BUG_ON(irq < FIRST_LEGACY_IRQ || | |
caa4868e | 64 | irq >= FIRST_LEGACY_IRQ + num_ictlrs * 32); |
d1d8c666 CC |
65 | |
66 | base = ictlr_reg_base[(irq - FIRST_LEGACY_IRQ) / 32]; | |
67 | mask = BIT((irq - FIRST_LEGACY_IRQ) % 32); | |
68 | ||
69 | __raw_writel(mask, base + reg); | |
70 | } | |
71 | ||
3524b70e CC |
72 | static void tegra_mask(struct irq_data *d) |
73 | { | |
d1d8c666 CC |
74 | if (d->irq < FIRST_LEGACY_IRQ) |
75 | return; | |
76 | ||
77 | tegra_irq_write_mask(d->irq, ICTLR_CPU_IER_CLR); | |
3524b70e | 78 | } |
460907bc | 79 | |
3524b70e | 80 | static void tegra_unmask(struct irq_data *d) |
460907bc | 81 | { |
d1d8c666 CC |
82 | if (d->irq < FIRST_LEGACY_IRQ) |
83 | return; | |
84 | ||
85 | tegra_irq_write_mask(d->irq, ICTLR_CPU_IER_SET); | |
460907bc | 86 | } |
460907bc | 87 | |
26d902c0 CC |
88 | static void tegra_ack(struct irq_data *d) |
89 | { | |
d1d8c666 CC |
90 | if (d->irq < FIRST_LEGACY_IRQ) |
91 | return; | |
92 | ||
93 | tegra_irq_write_mask(d->irq, ICTLR_CPU_IEP_FIR_CLR); | |
26d902c0 CC |
94 | } |
95 | ||
4bd66cfd CC |
96 | static void tegra_eoi(struct irq_data *d) |
97 | { | |
98 | if (d->irq < FIRST_LEGACY_IRQ) | |
99 | return; | |
100 | ||
101 | tegra_irq_write_mask(d->irq, ICTLR_CPU_IEP_FIR_CLR); | |
102 | } | |
103 | ||
26d902c0 CC |
104 | static int tegra_retrigger(struct irq_data *d) |
105 | { | |
d1d8c666 | 106 | if (d->irq < FIRST_LEGACY_IRQ) |
938fa349 CC |
107 | return 0; |
108 | ||
d1d8c666 CC |
109 | tegra_irq_write_mask(d->irq, ICTLR_CPU_IEP_FIR_SET); |
110 | ||
26d902c0 CC |
111 | return 1; |
112 | } | |
113 | ||
5ad36c5f EG |
114 | void __init tegra_init_irq(void) |
115 | { | |
d1d8c666 | 116 | int i; |
caa4868e PDS |
117 | void __iomem *distbase; |
118 | ||
119 | distbase = IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE); | |
120 | num_ictlrs = readl_relaxed(distbase + GIC_DIST_CTR) & 0x1f; | |
121 | ||
122 | if (num_ictlrs > ARRAY_SIZE(ictlr_reg_base)) { | |
123 | WARN(1, "Too many (%d) interrupt controllers found. Maximum is %d.", | |
124 | num_ictlrs, ARRAY_SIZE(ictlr_reg_base)); | |
125 | num_ictlrs = ARRAY_SIZE(ictlr_reg_base); | |
126 | } | |
d1d8c666 | 127 | |
caa4868e | 128 | for (i = 0; i < num_ictlrs; i++) { |
d1d8c666 CC |
129 | void __iomem *ictlr = ictlr_reg_base[i]; |
130 | writel(~0, ictlr + ICTLR_CPU_IER_CLR); | |
131 | writel(0, ictlr + ICTLR_CPU_IEP_CLASS); | |
132 | } | |
460907bc | 133 | |
938fa349 | 134 | gic_arch_extn.irq_ack = tegra_ack; |
4bd66cfd | 135 | gic_arch_extn.irq_eoi = tegra_eoi; |
938fa349 CC |
136 | gic_arch_extn.irq_mask = tegra_mask; |
137 | gic_arch_extn.irq_unmask = tegra_unmask; | |
138 | gic_arch_extn.irq_retrigger = tegra_retrigger; | |
139 | ||
0d4f7479 | 140 | /* |
141 | * Check if there is a devicetree present, since the GIC will be | |
142 | * initialized elsewhere under DT. | |
143 | */ | |
144 | if (!of_have_populated_dt()) | |
caa4868e | 145 | gic_init(0, 29, distbase, |
0d4f7479 | 146 | IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100)); |
460907bc | 147 | } |