2 * Copyright (C) 2000 David J. Mckay (david.mckay@st.com)
4 * May be copied or modified under the terms of the GNU General Public
5 * License. See linux/COPYING for more information.
7 * Looks after interrupts on the overdrive board.
9 * Bases on the IPR irq system
12 #include <linux/config.h>
13 #include <linux/init.h>
14 #include <linux/irq.h>
16 #include <asm/system.h>
19 #include <asm/overdrive/overdrive.h>
26 #define NUM_EXTERNAL_IRQS 16
27 #define EXTERNAL_IRQ_NOT_IN_USE (-1)
28 #define EXTERNAL_IRQ_NOT_ASSIGNED (-1)
31 * This table is used to determine what to program into the FPGA's CT register
32 * for the specified Linux IRQ.
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.
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
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
46 static 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 */
66 static void set_od_data(int overdrive_irq
, int irq
)
68 if (irq
>= NUM_EXTERNAL_IRQS
|| irq
< 0)
70 od_data_table
[irq
].overdrive_irq
= overdrive_irq
<< 2;
73 static void enable_od_irq(unsigned int irq
);
74 void disable_od_irq(unsigned int irq
);
76 /* shutdown is same as "disable" */
77 #define shutdown_od_irq disable_od_irq
79 static void mask_and_ack_od(unsigned int);
80 static void end_od_irq(unsigned int irq
);
82 static unsigned int startup_od_irq(unsigned int irq
)
85 return 0; /* never anything pending */
88 static struct hw_interrupt_type od_irq_type
= {
89 .typename
= "Overdrive-IRQ",
90 .startup
= startup_od_irq
,
91 .shutdown
= shutdown_od_irq
,
92 .enable
= enable_od_irq
,
93 .disable
= disable_od_irq
,
94 .ack
= mask_and_ack_od
,
98 static void disable_od_irq(unsigned int irq
)
104 /* Not a valid interrupt */
105 if (irq
< 0 || irq
>= NUM_EXTERNAL_IRQS
)
108 /* Is is necessary to use a cli here? Would a spinlock not be
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
);
117 ctrl_outl(val
, OVERDRIVE_INT_CT
);
119 local_irq_restore(flags
);
122 static void enable_od_irq(unsigned int irq
)
128 /* Not a valid interrupt */
129 if (irq
< 0 || irq
>= NUM_EXTERNAL_IRQS
)
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
);
140 mask
= od_data_table
[irq
].irq_mask
<< overdrive_irq
;
142 ctrl_outl(val
, OVERDRIVE_INT_CT
);
144 local_irq_restore(flags
);
149 /* this functions sets the desired irq handler to be an overdrive type */
150 static void __init
make_od_irq(unsigned int irq
)
152 disable_irq_nosync(irq
);
153 irq_desc
[irq
].handler
= &od_irq_type
;
158 static void mask_and_ack_od(unsigned int irq
)
163 static void end_od_irq(unsigned int irq
)
168 void __init
init_overdrive_irq(void)
172 /* Disable all interrupts */
173 ctrl_outl(0, OVERDRIVE_INT_CT
);
175 /* Update interrupt pin mode to use encoded interrupts */
176 i
= ctrl_inw(INTC_ICR
);
178 ctrl_outw(i
, INTC_ICR
);
180 for (i
= 0; i
< NUM_EXTERNAL_IRQS
; i
++) {
181 if (od_data_table
[i
].irq_mask
!= EXTERNAL_IRQ_NOT_IN_USE
) {
183 } else if (i
!= 15) { // Cannot use imask on level 15
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
);