diff options
Diffstat (limited to 'arch/mips/loongson32/common/irq.c')
-rw-r--r-- | arch/mips/loongson32/common/irq.c | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/arch/mips/loongson32/common/irq.c b/arch/mips/loongson32/common/irq.c new file mode 100644 index 000000000..9a50070f7 --- /dev/null +++ b/arch/mips/loongson32/common/irq.c | |||
@@ -0,0 +1,191 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
2 | /* | ||
3 | * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com> | ||
4 | */ | ||
5 | |||
6 | #include <linux/interrupt.h> | ||
7 | #include <linux/irq.h> | ||
8 | #include <asm/irq_cpu.h> | ||
9 | |||
10 | #include <loongson1.h> | ||
11 | #include <irq.h> | ||
12 | |||
13 | #define LS1X_INTC_REG(n, x) \ | ||
14 | ((void __iomem *)KSEG1ADDR(LS1X_INTC_BASE + (n * 0x18) + (x))) | ||
15 | |||
16 | #define LS1X_INTC_INTISR(n) LS1X_INTC_REG(n, 0x0) | ||
17 | #define LS1X_INTC_INTIEN(n) LS1X_INTC_REG(n, 0x4) | ||
18 | #define LS1X_INTC_INTSET(n) LS1X_INTC_REG(n, 0x8) | ||
19 | #define LS1X_INTC_INTCLR(n) LS1X_INTC_REG(n, 0xc) | ||
20 | #define LS1X_INTC_INTPOL(n) LS1X_INTC_REG(n, 0x10) | ||
21 | #define LS1X_INTC_INTEDGE(n) LS1X_INTC_REG(n, 0x14) | ||
22 | |||
23 | static void ls1x_irq_ack(struct irq_data *d) | ||
24 | { | ||
25 | unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f; | ||
26 | unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5; | ||
27 | |||
28 | __raw_writel(__raw_readl(LS1X_INTC_INTCLR(n)) | ||
29 | | (1 << bit), LS1X_INTC_INTCLR(n)); | ||
30 | } | ||
31 | |||
32 | static void ls1x_irq_mask(struct irq_data *d) | ||
33 | { | ||
34 | unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f; | ||
35 | unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5; | ||
36 | |||
37 | __raw_writel(__raw_readl(LS1X_INTC_INTIEN(n)) | ||
38 | & ~(1 << bit), LS1X_INTC_INTIEN(n)); | ||
39 | } | ||
40 | |||
41 | static void ls1x_irq_mask_ack(struct irq_data *d) | ||
42 | { | ||
43 | unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f; | ||
44 | unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5; | ||
45 | |||
46 | __raw_writel(__raw_readl(LS1X_INTC_INTIEN(n)) | ||
47 | & ~(1 << bit), LS1X_INTC_INTIEN(n)); | ||
48 | __raw_writel(__raw_readl(LS1X_INTC_INTCLR(n)) | ||
49 | | (1 << bit), LS1X_INTC_INTCLR(n)); | ||
50 | } | ||
51 | |||
52 | static void ls1x_irq_unmask(struct irq_data *d) | ||
53 | { | ||
54 | unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f; | ||
55 | unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5; | ||
56 | |||
57 | __raw_writel(__raw_readl(LS1X_INTC_INTIEN(n)) | ||
58 | | (1 << bit), LS1X_INTC_INTIEN(n)); | ||
59 | } | ||
60 | |||
61 | static int ls1x_irq_settype(struct irq_data *d, unsigned int type) | ||
62 | { | ||
63 | unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f; | ||
64 | unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5; | ||
65 | |||
66 | switch (type) { | ||
67 | case IRQ_TYPE_LEVEL_HIGH: | ||
68 | __raw_writel(__raw_readl(LS1X_INTC_INTPOL(n)) | ||
69 | | (1 << bit), LS1X_INTC_INTPOL(n)); | ||
70 | __raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n)) | ||
71 | & ~(1 << bit), LS1X_INTC_INTEDGE(n)); | ||
72 | break; | ||
73 | case IRQ_TYPE_LEVEL_LOW: | ||
74 | __raw_writel(__raw_readl(LS1X_INTC_INTPOL(n)) | ||
75 | & ~(1 << bit), LS1X_INTC_INTPOL(n)); | ||
76 | __raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n)) | ||
77 | & ~(1 << bit), LS1X_INTC_INTEDGE(n)); | ||
78 | break; | ||
79 | case IRQ_TYPE_EDGE_RISING: | ||
80 | __raw_writel(__raw_readl(LS1X_INTC_INTPOL(n)) | ||
81 | | (1 << bit), LS1X_INTC_INTPOL(n)); | ||
82 | __raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n)) | ||
83 | | (1 << bit), LS1X_INTC_INTEDGE(n)); | ||
84 | break; | ||
85 | case IRQ_TYPE_EDGE_FALLING: | ||
86 | __raw_writel(__raw_readl(LS1X_INTC_INTPOL(n)) | ||
87 | & ~(1 << bit), LS1X_INTC_INTPOL(n)); | ||
88 | __raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n)) | ||
89 | | (1 << bit), LS1X_INTC_INTEDGE(n)); | ||
90 | break; | ||
91 | case IRQ_TYPE_EDGE_BOTH: | ||
92 | __raw_writel(__raw_readl(LS1X_INTC_INTPOL(n)) | ||
93 | & ~(1 << bit), LS1X_INTC_INTPOL(n)); | ||
94 | __raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n)) | ||
95 | | (1 << bit), LS1X_INTC_INTEDGE(n)); | ||
96 | break; | ||
97 | case IRQ_TYPE_NONE: | ||
98 | break; | ||
99 | default: | ||
100 | return -EINVAL; | ||
101 | } | ||
102 | |||
103 | return 0; | ||
104 | } | ||
105 | |||
106 | static struct irq_chip ls1x_irq_chip = { | ||
107 | .name = "LS1X-INTC", | ||
108 | .irq_ack = ls1x_irq_ack, | ||
109 | .irq_mask = ls1x_irq_mask, | ||
110 | .irq_mask_ack = ls1x_irq_mask_ack, | ||
111 | .irq_unmask = ls1x_irq_unmask, | ||
112 | .irq_set_type = ls1x_irq_settype, | ||
113 | }; | ||
114 | |||
115 | static void ls1x_irq_dispatch(int n) | ||
116 | { | ||
117 | u32 int_status, irq; | ||
118 | |||
119 | /* Get pending sources, masked by current enables */ | ||
120 | int_status = __raw_readl(LS1X_INTC_INTISR(n)) & | ||
121 | __raw_readl(LS1X_INTC_INTIEN(n)); | ||
122 | |||
123 | if (int_status) { | ||
124 | irq = LS1X_IRQ(n, __ffs(int_status)); | ||
125 | do_IRQ(irq); | ||
126 | } | ||
127 | } | ||
128 | |||
129 | asmlinkage void plat_irq_dispatch(void) | ||
130 | { | ||
131 | unsigned int pending; | ||
132 | |||
133 | pending = read_c0_cause() & read_c0_status() & ST0_IM; | ||
134 | |||
135 | if (pending & CAUSEF_IP7) | ||
136 | do_IRQ(TIMER_IRQ); | ||
137 | else if (pending & CAUSEF_IP2) | ||
138 | ls1x_irq_dispatch(0); /* INT0 */ | ||
139 | else if (pending & CAUSEF_IP3) | ||
140 | ls1x_irq_dispatch(1); /* INT1 */ | ||
141 | else if (pending & CAUSEF_IP4) | ||
142 | ls1x_irq_dispatch(2); /* INT2 */ | ||
143 | else if (pending & CAUSEF_IP5) | ||
144 | ls1x_irq_dispatch(3); /* INT3 */ | ||
145 | else if (pending & CAUSEF_IP6) | ||
146 | ls1x_irq_dispatch(4); /* INT4 */ | ||
147 | else | ||
148 | spurious_interrupt(); | ||
149 | |||
150 | } | ||
151 | |||
152 | static void __init ls1x_irq_init(int base) | ||
153 | { | ||
154 | int n; | ||
155 | |||
156 | /* Disable interrupts and clear pending, | ||
157 | * setup all IRQs as high level triggered | ||
158 | */ | ||
159 | for (n = 0; n < INTN; n++) { | ||
160 | __raw_writel(0x0, LS1X_INTC_INTIEN(n)); | ||
161 | __raw_writel(0xffffffff, LS1X_INTC_INTCLR(n)); | ||
162 | __raw_writel(0xffffffff, LS1X_INTC_INTPOL(n)); | ||
163 | /* set DMA0, DMA1 and DMA2 to edge trigger */ | ||
164 | __raw_writel(n ? 0x0 : 0xe000, LS1X_INTC_INTEDGE(n)); | ||
165 | } | ||
166 | |||
167 | |||
168 | for (n = base; n < NR_IRQS; n++) { | ||
169 | irq_set_chip_and_handler(n, &ls1x_irq_chip, | ||
170 | handle_level_irq); | ||
171 | } | ||
172 | |||
173 | if (request_irq(INT0_IRQ, no_action, IRQF_NO_THREAD, "cascade", NULL)) | ||
174 | pr_err("Failed to request irq %d (cascade)\n", INT0_IRQ); | ||
175 | if (request_irq(INT1_IRQ, no_action, IRQF_NO_THREAD, "cascade", NULL)) | ||
176 | pr_err("Failed to request irq %d (cascade)\n", INT1_IRQ); | ||
177 | if (request_irq(INT2_IRQ, no_action, IRQF_NO_THREAD, "cascade", NULL)) | ||
178 | pr_err("Failed to request irq %d (cascade)\n", INT2_IRQ); | ||
179 | if (request_irq(INT3_IRQ, no_action, IRQF_NO_THREAD, "cascade", NULL)) | ||
180 | pr_err("Failed to request irq %d (cascade)\n", INT3_IRQ); | ||
181 | #if defined(CONFIG_LOONGSON1_LS1C) | ||
182 | if (request_irq(INT4_IRQ, no_action, IRQF_NO_THREAD, "cascade", NULL)) | ||
183 | pr_err("Failed to request irq %d (cascade)\n", INT4_IRQ); | ||
184 | #endif | ||
185 | } | ||
186 | |||
187 | void __init arch_init_irq(void) | ||
188 | { | ||
189 | mips_cpu_irq_init(); | ||
190 | ls1x_irq_init(LS1X_IRQ_BASE); | ||
191 | } | ||