Merge ../torvalds-2.6/
[pandora-kernel.git] / arch / sh / kernel / cpu / sh4 / irq_intc2.c
1 /*
2  * linux/arch/sh/kernel/irq_intc2.c
3  *
4  * Copyright (C) 2001 David J. Mckay (david.mckay@st.com)
5  *
6  * May be copied or modified under the terms of the GNU General Public
7  * License.  See linux/COPYING for more information.                            
8  *
9  * Interrupt handling for INTC2-based IRQ.
10  *
11  * These are the "new Hitachi style" interrupts, as present on the 
12  * Hitachi 7751 and the STM ST40 STB1.
13  */
14
15 #include <linux/kernel.h>
16 #include <linux/init.h>
17 #include <linux/irq.h>
18
19 #include <asm/system.h>
20 #include <asm/io.h>
21 #include <asm/machvec.h>
22
23
24 struct intc2_data {
25         unsigned char msk_offset;
26         unsigned char msk_shift;
27 #ifdef CONFIG_CPU_SUBTYPE_ST40
28         int (*clear_irq) (int);
29 #endif
30 };
31
32
33 static struct intc2_data intc2_data[NR_INTC2_IRQS];
34
35 static void enable_intc2_irq(unsigned int irq);
36 static void disable_intc2_irq(unsigned int irq);
37
38 /* shutdown is same as "disable" */
39 #define shutdown_intc2_irq disable_intc2_irq
40
41 static void mask_and_ack_intc2(unsigned int);
42 static void end_intc2_irq(unsigned int irq);
43
44 static unsigned int startup_intc2_irq(unsigned int irq)
45
46         enable_intc2_irq(irq);
47         return 0; /* never anything pending */
48 }
49
50 static struct hw_interrupt_type intc2_irq_type = {
51         .typename = "INTC2-IRQ",
52         .startup = startup_intc2_irq,
53         .shutdown = shutdown_intc2_irq,
54         .enable = enable_intc2_irq,
55         .disable = disable_intc2_irq,
56         .ack = mask_and_ack_intc2,
57         .end = end_intc2_irq
58 };
59
60 static void disable_intc2_irq(unsigned int irq)
61 {
62         int irq_offset = irq - INTC2_FIRST_IRQ;
63         int msk_shift, msk_offset;
64
65         // Sanity check
66         if((irq_offset<0) || (irq_offset>=NR_INTC2_IRQS))
67                 return;
68
69         msk_shift = intc2_data[irq_offset].msk_shift;
70         msk_offset = intc2_data[irq_offset].msk_offset;
71
72         ctrl_outl(1<<msk_shift,
73                   INTC2_BASE+INTC2_INTMSK_OFFSET+msk_offset);
74 }
75
76 static void enable_intc2_irq(unsigned int irq)
77 {
78         int irq_offset = irq - INTC2_FIRST_IRQ;
79         int msk_shift, msk_offset;
80
81         /* Sanity check */
82         if((irq_offset<0) || (irq_offset>=NR_INTC2_IRQS))
83                 return;
84
85         msk_shift = intc2_data[irq_offset].msk_shift;
86         msk_offset = intc2_data[irq_offset].msk_offset;
87
88         ctrl_outl(1<<msk_shift,
89                   INTC2_BASE+INTC2_INTMSKCLR_OFFSET+msk_offset);
90 }
91
92 static void mask_and_ack_intc2(unsigned int irq)
93 {
94         disable_intc2_irq(irq);
95 }
96
97 static void end_intc2_irq(unsigned int irq)
98 {
99         if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
100                 enable_intc2_irq(irq);
101
102 #ifdef CONFIG_CPU_SUBTYPE_ST40
103         if (intc2_data[irq - INTC2_FIRST_IRQ].clear_irq)
104                 intc2_data[irq - INTC2_FIRST_IRQ].clear_irq (irq);
105 #endif
106 }
107
108 /*
109  * Setup an INTC2 style interrupt.
110  * NOTE: Unlike IPR interrupts, parameters are not shifted by this code,
111  * allowing the use of the numbers straight out of the datasheet.
112  * For example:
113  *    PIO1 which is INTPRI00[19,16] and INTMSK00[13]
114  * would be:               ^     ^             ^  ^
115  *                         |     |             |  |
116  *    make_intc2_irq(84,   0,   16,            0, 13);
117  */
118 void make_intc2_irq(unsigned int irq,
119                     unsigned int ipr_offset, unsigned int ipr_shift,
120                     unsigned int msk_offset, unsigned int msk_shift,
121                     unsigned int priority)
122 {
123         int irq_offset = irq - INTC2_FIRST_IRQ;
124         unsigned int flags;
125         unsigned long ipr;
126
127         if((irq_offset<0) || (irq_offset>=NR_INTC2_IRQS))
128                 return;
129
130         disable_irq_nosync(irq);
131
132         /* Fill the data we need */
133         intc2_data[irq_offset].msk_offset = msk_offset;
134         intc2_data[irq_offset].msk_shift  = msk_shift;
135 #ifdef CONFIG_CPU_SUBTYPE_ST40
136         intc2_data[irq_offset].clear_irq = NULL;
137 #endif
138                 
139         /* Set the priority level */
140         local_irq_save(flags);
141
142         ipr=ctrl_inl(INTC2_BASE+INTC2_INTPRI_OFFSET+ipr_offset);
143         ipr&=~(0xf<<ipr_shift);
144         ipr|=(priority)<<ipr_shift;
145         ctrl_outl(ipr, INTC2_BASE+INTC2_INTPRI_OFFSET+ipr_offset);
146
147         local_irq_restore(flags);
148
149         irq_desc[irq].handler=&intc2_irq_type;
150
151         disable_intc2_irq(irq);
152 }
153
154 #ifdef CONFIG_CPU_SUBTYPE_ST40
155
156 struct intc2_init {
157         unsigned short irq;
158         unsigned char ipr_offset, ipr_shift;
159         unsigned char msk_offset, msk_shift;
160 };
161
162 static struct intc2_init intc2_init_data[]  __initdata = {
163         {64,  0,  0, 0,  0},    /* PCI serr */
164         {65,  0,  4, 0,  1},    /* PCI err */
165         {66,  0,  4, 0,  2},    /* PCI ad */
166         {67,  0,  4, 0,  3},    /* PCI pwd down */
167         {72,  0,  8, 0,  5},    /* DMAC INT0 */
168         {73,  0,  8, 0,  6},    /* DMAC INT1 */
169         {74,  0,  8, 0,  7},    /* DMAC INT2 */
170         {75,  0,  8, 0,  8},    /* DMAC INT3 */
171         {76,  0,  8, 0,  9},    /* DMAC INT4 */
172         {78,  0,  8, 0, 11},    /* DMAC ERR */
173         {80,  0, 12, 0, 12},    /* PIO0 */
174         {84,  0, 16, 0, 13},    /* PIO1 */
175         {88,  0, 20, 0, 14},    /* PIO2 */
176         {112, 4,  0, 4,  0},    /* Mailbox */
177 #ifdef CONFIG_CPU_SUBTYPE_ST40GX1
178         {116, 4,  4, 4,  4},    /* SSC0 */
179         {120, 4,  8, 4,  8},    /* IR Blaster */
180         {124, 4, 12, 4, 12},    /* USB host */
181         {128, 4, 16, 4, 16},    /* Video processor BLITTER */
182         {132, 4, 20, 4, 20},    /* UART0 */
183         {134, 4, 20, 4, 22},    /* UART2 */
184         {136, 4, 24, 4, 24},    /* IO_PIO0 */
185         {140, 4, 28, 4, 28},    /* EMPI */
186         {144, 8,  0, 8,  0},    /* MAFE */
187         {148, 8,  4, 8,  4},    /* PWM */
188         {152, 8,  8, 8,  8},    /* SSC1 */
189         {156, 8, 12, 8, 12},    /* IO_PIO1 */
190         {160, 8, 16, 8, 16},    /* USB target */
191         {164, 8, 20, 8, 20},    /* UART1 */
192         {168, 8, 24, 8, 24},    /* Teletext */
193         {172, 8, 28, 8, 28},    /* VideoSync VTG */
194         {173, 8, 28, 8, 29},    /* VideoSync DVP0 */
195         {174, 8, 28, 8, 30},    /* VideoSync DVP1 */
196 #endif
197 };
198
199 void __init init_IRQ_intc2(void)
200 {
201         struct intc2_init *p;
202
203         printk(KERN_ALERT "init_IRQ_intc2\n");
204
205         for (p = intc2_init_data;
206              p<intc2_init_data+ARRAY_SIZE(intc2_init_data);
207              p++) {
208                 make_intc2_irq(p->irq, p->ipr_offset, p->ipr_shift,
209                                p-> msk_offset, p->msk_shift, 13);
210         }
211 }
212
213 /* Adds a termination callback to the interrupt */
214 void intc2_add_clear_irq(int irq, int (*fn)(int))
215 {
216         if (irq < INTC2_FIRST_IRQ)
217                 return;
218
219         intc2_data[irq - INTC2_FIRST_IRQ].clear_irq = fn;
220 }
221
222 #endif /* CONFIG_CPU_SUBTYPE_ST40 */