aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sh/boards/overdrive/irq.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sh/boards/overdrive/irq.c')
-rw-r--r--arch/sh/boards/overdrive/irq.c192
1 files changed, 192 insertions, 0 deletions
diff --git a/arch/sh/boards/overdrive/irq.c b/arch/sh/boards/overdrive/irq.c
new file mode 100644
index 000000000000..23adc6be71e7
--- /dev/null
+++ b/arch/sh/boards/overdrive/irq.c
@@ -0,0 +1,192 @@
1/*
2 * Copyright (C) 2000 David J. Mckay (david.mckay@st.com)
3 *
4 * May be copied or modified under the terms of the GNU General Public
5 * License. See linux/COPYING for more information.
6 *
7 * Looks after interrupts on the overdrive board.
8 *
9 * Bases on the IPR irq system
10 */
11
12#include <linux/config.h>
13#include <linux/init.h>
14#include <linux/irq.h>
15
16#include <asm/system.h>
17#include <asm/io.h>
18
19#include <asm/overdrive/overdrive.h>
20
21struct od_data {
22 int overdrive_irq;
23 int irq_mask;
24};
25
26#define NUM_EXTERNAL_IRQS 16
27#define EXTERNAL_IRQ_NOT_IN_USE (-1)
28#define EXTERNAL_IRQ_NOT_ASSIGNED (-1)
29
30/*
31 * This table is used to determine what to program into the FPGA's CT register
32 * for the specified Linux IRQ.
33 *
34 * The irq_mask gives the interrupt number from the PCI board (PCI_Int(6:0))
35 * but is one greater than that because the because the FPGA treats 0
36 * as disabled, a value of 1 asserts PCI_Int0, and so on.
37 *
38 * The overdrive_irq specifies which of the eight interrupt sources generates
39 * that interrupt, and but is multiplied by four to give the bit offset into
40 * the CT register.
41 *
42 * The seven interrupts levels (SH4 IRL's) we have available here is hardwired
43 * by the EPLD. The assignments here of which PCI interrupt generates each
44 * level is arbitary.
45 */
46static struct od_data od_data_table[NUM_EXTERNAL_IRQS] = {
47 /* overdrive_irq , irq_mask */
48 {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE}, /* 0 */
49 {EXTERNAL_IRQ_NOT_ASSIGNED, 7}, /* 1 */
50 {EXTERNAL_IRQ_NOT_ASSIGNED, 6}, /* 2 */
51 {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE}, /* 3 */
52 {EXTERNAL_IRQ_NOT_ASSIGNED, 5}, /* 4 */
53 {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE}, /* 5 */
54 {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE}, /* 6 */
55 {EXTERNAL_IRQ_NOT_ASSIGNED, 4}, /* 7 */
56 {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE}, /* 8 */
57 {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE}, /* 9 */
58 {EXTERNAL_IRQ_NOT_ASSIGNED, 3}, /* 10 */
59 {EXTERNAL_IRQ_NOT_ASSIGNED, 2}, /* 11 */
60 {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE}, /* 12 */
61 {EXTERNAL_IRQ_NOT_ASSIGNED, 1}, /* 13 */
62 {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE}, /* 14 */
63 {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE} /* 15 */
64};
65
66static void set_od_data(int overdrive_irq, int irq)
67{
68 if (irq >= NUM_EXTERNAL_IRQS || irq < 0)
69 return;
70 od_data_table[irq].overdrive_irq = overdrive_irq << 2;
71}
72
73static void enable_od_irq(unsigned int irq);
74void disable_od_irq(unsigned int irq);
75
76/* shutdown is same as "disable" */
77#define shutdown_od_irq disable_od_irq
78
79static void mask_and_ack_od(unsigned int);
80static void end_od_irq(unsigned int irq);
81
82static unsigned int startup_od_irq(unsigned int irq)
83{
84 enable_od_irq(irq);
85 return 0; /* never anything pending */
86}
87
88static struct hw_interrupt_type od_irq_type = {
89 "Overdrive-IRQ",
90 startup_od_irq,
91 shutdown_od_irq,
92 enable_od_irq,
93 disable_od_irq,
94 mask_and_ack_od,
95 end_od_irq
96};
97
98static void disable_od_irq(unsigned int irq)
99{
100 unsigned val, flags;
101 int overdrive_irq;
102 unsigned mask;
103
104 /* Not a valid interrupt */
105 if (irq < 0 || irq >= NUM_EXTERNAL_IRQS)
106 return;
107
108 /* Is is necessary to use a cli here? Would a spinlock not be
109 * mroe efficient?
110 */
111 local_irq_save(flags);
112 overdrive_irq = od_data_table[irq].overdrive_irq;
113 if (overdrive_irq != EXTERNAL_IRQ_NOT_ASSIGNED) {
114 mask = ~(0x7 << overdrive_irq);
115 val = ctrl_inl(OVERDRIVE_INT_CT);
116 val &= mask;
117 ctrl_outl(val, OVERDRIVE_INT_CT);
118 }
119 local_irq_restore(flags);
120}
121
122static void enable_od_irq(unsigned int irq)
123{
124 unsigned val, flags;
125 int overdrive_irq;
126 unsigned mask;
127
128 /* Not a valid interrupt */
129 if (irq < 0 || irq >= NUM_EXTERNAL_IRQS)
130 return;
131
132 /* Set priority in OD back to original value */
133 local_irq_save(flags);
134 /* This one is not in use currently */
135 overdrive_irq = od_data_table[irq].overdrive_irq;
136 if (overdrive_irq != EXTERNAL_IRQ_NOT_ASSIGNED) {
137 val = ctrl_inl(OVERDRIVE_INT_CT);
138 mask = ~(0x7 << overdrive_irq);
139 val &= mask;
140 mask = od_data_table[irq].irq_mask << overdrive_irq;
141 val |= mask;
142 ctrl_outl(val, OVERDRIVE_INT_CT);
143 }
144 local_irq_restore(flags);
145}
146
147
148
149/* this functions sets the desired irq handler to be an overdrive type */
150static void __init make_od_irq(unsigned int irq)
151{
152 disable_irq_nosync(irq);
153 irq_desc[irq].handler = &od_irq_type;
154 disable_od_irq(irq);
155}
156
157
158static void mask_and_ack_od(unsigned int irq)
159{
160 disable_od_irq(irq);
161}
162
163static void end_od_irq(unsigned int irq)
164{
165 enable_od_irq(irq);
166}
167
168void __init init_overdrive_irq(void)
169{
170 int i;
171
172 /* Disable all interrupts */
173 ctrl_outl(0, OVERDRIVE_INT_CT);
174
175 /* Update interrupt pin mode to use encoded interrupts */
176 i = ctrl_inw(INTC_ICR);
177 i &= ~INTC_ICR_IRLM;
178 ctrl_outw(i, INTC_ICR);
179
180 for (i = 0; i < NUM_EXTERNAL_IRQS; i++) {
181 if (od_data_table[i].irq_mask != EXTERNAL_IRQ_NOT_IN_USE) {
182 make_od_irq(i);
183 } else if (i != 15) { // Cannot use imask on level 15
184 make_imask_irq(i);
185 }
186 }
187
188 /* Set up the interrupts */
189 set_od_data(OVERDRIVE_PCI_INTA, OVERDRIVE_PCI_IRQ1);
190 set_od_data(OVERDRIVE_PCI_INTB, OVERDRIVE_PCI_IRQ2);
191 set_od_data(OVERDRIVE_AUDIO_INT, OVERDRIVE_ESS_IRQ);
192}