Merge git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6
[linux-block.git] / arch / sh / boards / overdrive / irq.c
CommitLineData
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
20struct 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 */
45static 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
65static 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
72static void enable_od_irq(unsigned int irq);
73void disable_od_irq(unsigned int irq);
74
75/* shutdown is same as "disable" */
76#define shutdown_od_irq disable_od_irq
77
78static void mask_and_ack_od(unsigned int);
79static void end_od_irq(unsigned int irq);
80
81static unsigned int startup_od_irq(unsigned int irq)
82{
83 enable_od_irq(irq);
84 return 0; /* never anything pending */
85}
86
87static 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
97static 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
121static 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 */
149static 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
157static void mask_and_ack_od(unsigned int irq)
158{
159 disable_od_irq(irq);
160}
161
162static void end_od_irq(unsigned int irq)
163{
164 enable_od_irq(irq);
165}
166
167void __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}