Merge branch 'for-linus' of master.kernel.org:/pub/scm/linux/kernel/git/roland/infiniband
[linux-2.6] / arch / powerpc / sysdev / mpc8xx_pic.c
1 #include <linux/kernel.h>
2 #include <linux/module.h>
3 #include <linux/stddef.h>
4 #include <linux/init.h>
5 #include <linux/sched.h>
6 #include <linux/signal.h>
7 #include <linux/irq.h>
8 #include <linux/dma-mapping.h>
9 #include <asm/prom.h>
10 #include <asm/irq.h>
11 #include <asm/io.h>
12 #include <asm/8xx_immap.h>
13 #include <asm/mpc8xx.h>
14
15 #include "mpc8xx_pic.h"
16
17
18 #define PIC_VEC_SPURRIOUS      15
19
20 extern int cpm_get_irq(struct pt_regs *regs);
21
22 static struct device_node *mpc8xx_pic_node;
23 static struct irq_host *mpc8xx_pic_host;
24 #define NR_MASK_WORDS   ((NR_IRQS + 31) / 32)
25 static unsigned long ppc_cached_irq_mask[NR_MASK_WORDS];
26 static sysconf8xx_t     *siu_reg;
27
28 int cpm_get_irq(struct pt_regs *regs);
29
30 static void mpc8xx_unmask_irq(unsigned int virq)
31 {
32         int     bit, word;
33         unsigned int irq_nr = (unsigned int)irq_map[virq].hwirq;
34
35         bit = irq_nr & 0x1f;
36         word = irq_nr >> 5;
37
38         ppc_cached_irq_mask[word] |= (1 << (31-bit));
39         out_be32(&siu_reg->sc_simask, ppc_cached_irq_mask[word]);
40 }
41
42 static void mpc8xx_mask_irq(unsigned int virq)
43 {
44         int     bit, word;
45         unsigned int irq_nr = (unsigned int)irq_map[virq].hwirq;
46
47         bit = irq_nr & 0x1f;
48         word = irq_nr >> 5;
49
50         ppc_cached_irq_mask[word] &= ~(1 << (31-bit));
51         out_be32(&siu_reg->sc_simask, ppc_cached_irq_mask[word]);
52 }
53
54 static void mpc8xx_ack(unsigned int virq)
55 {
56         int     bit;
57         unsigned int irq_nr = (unsigned int)irq_map[virq].hwirq;
58
59         bit = irq_nr & 0x1f;
60         out_be32(&siu_reg->sc_sipend, 1 << (31-bit));
61 }
62
63 static void mpc8xx_end_irq(unsigned int virq)
64 {
65         int bit, word;
66         unsigned int irq_nr = (unsigned int)irq_map[virq].hwirq;
67
68         bit = irq_nr & 0x1f;
69         word = irq_nr >> 5;
70
71         ppc_cached_irq_mask[word] |= (1 << (31-bit));
72         out_be32(&siu_reg->sc_simask, ppc_cached_irq_mask[word]);
73 }
74
75 static int mpc8xx_set_irq_type(unsigned int virq, unsigned int flow_type)
76 {
77         struct irq_desc *desc = get_irq_desc(virq);
78
79         desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
80         desc->status |= flow_type & IRQ_TYPE_SENSE_MASK;
81         if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
82                 desc->status |= IRQ_LEVEL;
83
84         if (flow_type & IRQ_TYPE_EDGE_FALLING) {
85                 irq_hw_number_t hw = (unsigned int)irq_map[virq].hwirq;
86                 unsigned int siel = in_be32(&siu_reg->sc_siel);
87
88                 /* only external IRQ senses are programmable */
89                 if ((hw & 1) == 0) {
90                         siel |= (0x80000000 >> hw);
91                         out_be32(&siu_reg->sc_siel, siel);
92                         desc->handle_irq = handle_edge_irq;
93                 }
94         }
95         return 0;
96 }
97
98 static struct irq_chip mpc8xx_pic = {
99         .typename = " MPC8XX SIU ",
100         .unmask = mpc8xx_unmask_irq,
101         .mask = mpc8xx_mask_irq,
102         .ack = mpc8xx_ack,
103         .eoi = mpc8xx_end_irq,
104         .set_type = mpc8xx_set_irq_type,
105 };
106
107 unsigned int mpc8xx_get_irq(void)
108 {
109         int irq;
110
111         /* For MPC8xx, read the SIVEC register and shift the bits down
112          * to get the irq number.
113          */
114         irq = in_be32(&siu_reg->sc_sivec) >> 26;
115
116         if (irq == PIC_VEC_SPURRIOUS)
117                 irq = NO_IRQ;
118
119         return irq_linear_revmap(mpc8xx_pic_host, irq);
120
121 }
122
123 static int mpc8xx_pic_host_match(struct irq_host *h, struct device_node *node)
124 {
125         return mpc8xx_pic_node == node;
126 }
127
128 static int mpc8xx_pic_host_map(struct irq_host *h, unsigned int virq,
129                           irq_hw_number_t hw)
130 {
131         pr_debug("mpc8xx_pic_host_map(%d, 0x%lx)\n", virq, hw);
132
133         /* Set default irq handle */
134         set_irq_chip_and_handler(virq, &mpc8xx_pic, handle_level_irq);
135         return 0;
136 }
137
138
139 static int mpc8xx_pic_host_xlate(struct irq_host *h, struct device_node *ct,
140                             u32 *intspec, unsigned int intsize,
141                             irq_hw_number_t *out_hwirq, unsigned int *out_flags)
142 {
143         static unsigned char map_pic_senses[4] = {
144                 IRQ_TYPE_EDGE_RISING,
145                 IRQ_TYPE_LEVEL_LOW,
146                 IRQ_TYPE_LEVEL_HIGH,
147                 IRQ_TYPE_EDGE_FALLING,
148         };
149
150         *out_hwirq = intspec[0];
151         if (intsize > 1 && intspec[1] < 4)
152                 *out_flags = map_pic_senses[intspec[1]];
153         else
154                 *out_flags = IRQ_TYPE_NONE;
155
156         return 0;
157 }
158
159
160 static struct irq_host_ops mpc8xx_pic_host_ops = {
161         .match = mpc8xx_pic_host_match,
162         .map = mpc8xx_pic_host_map,
163         .xlate = mpc8xx_pic_host_xlate,
164 };
165
166 int mpc8xx_pic_init(void)
167 {
168         struct resource res;
169         struct device_node *np = NULL;
170         int ret;
171
172         np = of_find_node_by_type(np, "mpc8xx-pic");
173
174         if (np == NULL) {
175                 printk(KERN_ERR "Could not find open-pic node\n");
176                 return -ENOMEM;
177         }
178
179         mpc8xx_pic_node = of_node_get(np);
180
181         ret = of_address_to_resource(np, 0, &res);
182         of_node_put(np);
183         if (ret)
184                 return ret;
185
186         siu_reg = (void *)ioremap(res.start, res.end - res.start + 1);
187         if (siu_reg == NULL)
188                 return -EINVAL;
189
190         mpc8xx_pic_host = irq_alloc_host(IRQ_HOST_MAP_LINEAR, 64, &mpc8xx_pic_host_ops, 64);
191         if (mpc8xx_pic_host == NULL) {
192                 printk(KERN_ERR "MPC8xx PIC: failed to allocate irq host!\n");
193                 ret = -ENOMEM;
194         }
195
196         return ret;
197 }