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