]> pilppa.org Git - linux-2.6-omap-h63xx.git/blob - arch/ppc64/kernel/iSeries_irq.c
[PATCH] ppc64 iSeries: remove XmPciLpEvent.c
[linux-2.6-omap-h63xx.git] / arch / ppc64 / kernel / iSeries_irq.c
1 /*
2  * This module supports the iSeries PCI bus interrupt handling
3  * Copyright (C) 20yy  <Robert L Holtorf> <IBM Corp>
4  * Copyright (C) 2004-2005 IBM Corporation
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the:
18  * Free Software Foundation, Inc.,
19  * 59 Temple Place, Suite 330,
20  * Boston, MA  02111-1307  USA
21  *
22  * Change Activity:
23  *   Created, December 13, 2000 by Wayne Holm
24  * End Change Activity
25  */
26 #include <linux/config.h>
27 #include <linux/pci.h>
28 #include <linux/init.h>
29 #include <linux/threads.h>
30 #include <linux/smp.h>
31 #include <linux/param.h>
32 #include <linux/string.h>
33 #include <linux/bootmem.h>
34 #include <linux/ide.h>
35 #include <linux/irq.h>
36 #include <linux/spinlock.h>
37
38 #include <asm/ppcdebug.h>
39 #include <asm/iSeries/HvTypes.h>
40 #include <asm/iSeries/HvLpEvent.h>
41 #include <asm/iSeries/HvCallPci.h>
42 #include <asm/iSeries/HvCallXm.h>
43 #include <asm/iSeries/iSeries_irq.h>
44
45 /* This maps virtual irq numbers to real irqs */
46 unsigned int virt_irq_to_real_map[NR_IRQS];
47
48 /* The next available virtual irq number */
49 /* Note: the pcnet32 driver assumes irq numbers < 2 aren't valid. :( */
50 static int next_virtual_irq = 2;
51
52 static long Pci_Interrupt_Count;
53 static long Pci_Event_Count;
54
55 enum XmPciLpEvent_Subtype {
56         XmPciLpEvent_BusCreated         = 0,    // PHB has been created
57         XmPciLpEvent_BusError           = 1,    // PHB has failed
58         XmPciLpEvent_BusFailed          = 2,    // Msg to Secondary, Primary failed bus
59         XmPciLpEvent_NodeFailed         = 4,    // Multi-adapter bridge has failed
60         XmPciLpEvent_NodeRecovered      = 5,    // Multi-adapter bridge has recovered
61         XmPciLpEvent_BusRecovered       = 12,   // PHB has been recovered
62         XmPciLpEvent_UnQuiesceBus       = 18,   // Secondary bus unqiescing
63         XmPciLpEvent_BridgeError        = 21,   // Bridge Error
64         XmPciLpEvent_SlotInterrupt      = 22    // Slot interrupt
65 };
66
67 struct XmPciLpEvent_BusInterrupt {
68         HvBusNumber     busNumber;
69         HvSubBusNumber  subBusNumber;
70 };
71
72 struct XmPciLpEvent_NodeInterrupt {
73         HvBusNumber     busNumber;
74         HvSubBusNumber  subBusNumber;
75         HvAgentId       deviceId;
76 };
77
78 struct XmPciLpEvent {
79         struct HvLpEvent hvLpEvent;
80
81         union {
82                 u64 alignData;                  // Align on an 8-byte boundary
83
84                 struct {
85                         u32             fisr;
86                         HvBusNumber     busNumber;
87                         HvSubBusNumber  subBusNumber;
88                         HvAgentId       deviceId;
89                 } slotInterrupt;
90
91                 struct XmPciLpEvent_BusInterrupt busFailed;
92                 struct XmPciLpEvent_BusInterrupt busRecovered;
93                 struct XmPciLpEvent_BusInterrupt busCreated;
94
95                 struct XmPciLpEvent_NodeInterrupt nodeFailed;
96                 struct XmPciLpEvent_NodeInterrupt nodeRecovered;
97
98         } eventData;
99
100 };
101
102 static void intReceived(struct XmPciLpEvent *eventParm,
103                 struct pt_regs *regsParm)
104 {
105         int irq;
106
107         ++Pci_Interrupt_Count;
108 #if 0
109         PPCDBG(PPCDBG_BUSWALK, "PCI: XmPciLpEvent.c: intReceived\n");
110 #endif
111
112         switch (eventParm->hvLpEvent.xSubtype) {
113         case XmPciLpEvent_SlotInterrupt:
114                 irq = eventParm->hvLpEvent.xCorrelationToken;
115                 /* Dispatch the interrupt handlers for this irq */
116                 ppc_irq_dispatch_handler(regsParm, irq);
117                 HvCallPci_eoi(eventParm->eventData.slotInterrupt.busNumber,
118                         eventParm->eventData.slotInterrupt.subBusNumber,
119                         eventParm->eventData.slotInterrupt.deviceId);
120                 break;
121                 /* Ignore error recovery events for now */
122         case XmPciLpEvent_BusCreated:
123                 printk(KERN_INFO "XmPciLpEvent.c: system bus %d created\n",
124                         eventParm->eventData.busCreated.busNumber);
125                 break;
126         case XmPciLpEvent_BusError:
127         case XmPciLpEvent_BusFailed:
128                 printk(KERN_INFO "XmPciLpEvent.c: system bus %d failed\n",
129                         eventParm->eventData.busFailed.busNumber);
130                 break;
131         case XmPciLpEvent_BusRecovered:
132         case XmPciLpEvent_UnQuiesceBus:
133                 printk(KERN_INFO "XmPciLpEvent.c: system bus %d recovered\n",
134                         eventParm->eventData.busRecovered.busNumber);
135                 break;
136         case XmPciLpEvent_NodeFailed:
137         case XmPciLpEvent_BridgeError:
138                 printk(KERN_INFO
139                         "XmPciLpEvent.c: multi-adapter bridge %d/%d/%d failed\n",
140                         eventParm->eventData.nodeFailed.busNumber,
141                         eventParm->eventData.nodeFailed.subBusNumber,
142                         eventParm->eventData.nodeFailed.deviceId);
143                 break;
144         case XmPciLpEvent_NodeRecovered:
145                 printk(KERN_INFO
146                         "XmPciLpEvent.c: multi-adapter bridge %d/%d/%d recovered\n",
147                         eventParm->eventData.nodeRecovered.busNumber,
148                         eventParm->eventData.nodeRecovered.subBusNumber,
149                         eventParm->eventData.nodeRecovered.deviceId);
150                 break;
151         default:
152                 printk(KERN_ERR
153                         "XmPciLpEvent.c: unrecognized event subtype 0x%x\n",
154                         eventParm->hvLpEvent.xSubtype);
155                 break;
156         }
157 }
158
159 static void XmPciLpEvent_handler(struct HvLpEvent *eventParm,
160                 struct pt_regs *regsParm)
161 {
162 #ifdef CONFIG_PCI
163 #if 0
164         PPCDBG(PPCDBG_BUSWALK, "XmPciLpEvent_handler, type 0x%x\n",
165                         eventParm->xType);
166 #endif
167         ++Pci_Event_Count;
168
169         if (eventParm && (eventParm->xType == HvLpEvent_Type_PciIo)) {
170                 switch (eventParm->xFlags.xFunction) {
171                 case HvLpEvent_Function_Int:
172                         intReceived((struct XmPciLpEvent *)eventParm, regsParm);
173                         break;
174                 case HvLpEvent_Function_Ack:
175                         printk(KERN_ERR
176                                 "XmPciLpEvent.c: unexpected ack received\n");
177                         break;
178                 default:
179                         printk(KERN_ERR
180                                 "XmPciLpEvent.c: unexpected event function %d\n",
181                                 (int)eventParm->xFlags.xFunction);
182                         break;
183                 }
184         } else if (eventParm)
185                 printk(KERN_ERR
186                         "XmPciLpEvent.c: Unrecognized PCI event type 0x%x\n",
187                         (int)eventParm->xType);
188         else
189                 printk(KERN_ERR "XmPciLpEvent.c: NULL event received\n");
190 #endif
191 }
192
193 /* This should be called sometime prior to buswalk (init_IRQ would be good) */
194 int XmPciLpEvent_init()
195 {
196         int xRc;
197
198         PPCDBG(PPCDBG_BUSWALK,
199                         "XmPciLpEvent_init, Register Event type 0x%04X\n",
200                         HvLpEvent_Type_PciIo);
201
202         xRc = HvLpEvent_registerHandler(HvLpEvent_Type_PciIo,
203                         &XmPciLpEvent_handler);
204         if (xRc == 0) {
205                 xRc = HvLpEvent_openPath(HvLpEvent_Type_PciIo, 0);
206                 if (xRc != 0)
207                         printk(KERN_ERR "XmPciLpEvent.c: open event path "
208                                         "failed with rc 0x%x\n", xRc);
209         } else
210                 printk(KERN_ERR "XmPciLpEvent.c: register handler "
211                                 "failed with rc 0x%x\n", xRc);
212         return xRc;
213 }
214
215 /* This is called by init_IRQ.  set in ppc_md.init_IRQ by iSeries_setup.c */
216 void __init iSeries_init_IRQ(void)
217 {
218         /* Register PCI event handler and open an event path */
219         XmPciLpEvent_init();
220 }
221
222 #define REAL_IRQ_TO_BUS(irq)    ((((irq) >> 6) & 0xff) + 1)
223 #define REAL_IRQ_TO_IDSEL(irq)  ((((irq) >> 3) & 7) + 1)
224 #define REAL_IRQ_TO_FUNC(irq)   ((irq) & 7)
225
226 /*
227  * This will be called by device drivers (via enable_IRQ)
228  * to enable INTA in the bridge interrupt status register.
229  */
230 static void iSeries_enable_IRQ(unsigned int irq)
231 {
232         u32 bus, deviceId, function, mask;
233         const u32 subBus = 0;
234         unsigned int rirq = virt_irq_to_real_map[irq];
235
236         /* The IRQ has already been locked by the caller */
237         bus = REAL_IRQ_TO_BUS(rirq);
238         function = REAL_IRQ_TO_FUNC(rirq);
239         deviceId = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function;
240
241         /* Unmask secondary INTA */
242         mask = 0x80000000;
243         HvCallPci_unmaskInterrupts(bus, subBus, deviceId, mask);
244         PPCDBG(PPCDBG_BUSWALK, "iSeries_enable_IRQ 0x%02X.%02X.%02X 0x%04X\n",
245                         bus, subBus, deviceId, irq);
246 }
247
248 /* This is called by iSeries_activate_IRQs */
249 static unsigned int iSeries_startup_IRQ(unsigned int irq)
250 {
251         u32 bus, deviceId, function, mask;
252         const u32 subBus = 0;
253         unsigned int rirq = virt_irq_to_real_map[irq];
254
255         bus = REAL_IRQ_TO_BUS(rirq);
256         function = REAL_IRQ_TO_FUNC(rirq);
257         deviceId = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function;
258
259         /* Link the IRQ number to the bridge */
260         HvCallXm_connectBusUnit(bus, subBus, deviceId, irq);
261
262         /* Unmask bridge interrupts in the FISR */
263         mask = 0x01010000 << function;
264         HvCallPci_unmaskFisr(bus, subBus, deviceId, mask);
265         iSeries_enable_IRQ(irq);
266         return 0;
267 }
268
269 /*
270  * This is called out of iSeries_fixup to activate interrupt
271  * generation for usable slots
272  */
273 void __init iSeries_activate_IRQs()
274 {
275         int irq;
276         unsigned long flags;
277
278         for_each_irq (irq) {
279                 irq_desc_t *desc = get_irq_desc(irq);
280
281                 if (desc && desc->handler && desc->handler->startup) {
282                         spin_lock_irqsave(&desc->lock, flags);
283                         desc->handler->startup(irq);
284                         spin_unlock_irqrestore(&desc->lock, flags);
285                 }
286         }
287 }
288
289 /*  this is not called anywhere currently */
290 static void iSeries_shutdown_IRQ(unsigned int irq)
291 {
292         u32 bus, deviceId, function, mask;
293         const u32 subBus = 0;
294         unsigned int rirq = virt_irq_to_real_map[irq];
295
296         /* irq should be locked by the caller */
297         bus = REAL_IRQ_TO_BUS(rirq);
298         function = REAL_IRQ_TO_FUNC(rirq);
299         deviceId = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function;
300
301         /* Invalidate the IRQ number in the bridge */
302         HvCallXm_connectBusUnit(bus, subBus, deviceId, 0);
303
304         /* Mask bridge interrupts in the FISR */
305         mask = 0x01010000 << function;
306         HvCallPci_maskFisr(bus, subBus, deviceId, mask);
307 }
308
309 /*
310  * This will be called by device drivers (via disable_IRQ)
311  * to disable INTA in the bridge interrupt status register.
312  */
313 static void iSeries_disable_IRQ(unsigned int irq)
314 {
315         u32 bus, deviceId, function, mask;
316         const u32 subBus = 0;
317         unsigned int rirq = virt_irq_to_real_map[irq];
318
319         /* The IRQ has already been locked by the caller */
320         bus = REAL_IRQ_TO_BUS(rirq);
321         function = REAL_IRQ_TO_FUNC(rirq);
322         deviceId = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function;
323
324         /* Mask secondary INTA   */
325         mask = 0x80000000;
326         HvCallPci_maskInterrupts(bus, subBus, deviceId, mask);
327         PPCDBG(PPCDBG_BUSWALK, "iSeries_disable_IRQ 0x%02X.%02X.%02X 0x%04X\n",
328                         bus, subBus, deviceId, irq);
329 }
330
331 /*
332  * Need to define this so ppc_irq_dispatch_handler will NOT call
333  * enable_IRQ at the end of interrupt handling.  However, this does
334  * nothing because there is not enough information provided to do
335  * the EOI HvCall.  This is done by XmPciLpEvent.c
336  */
337 static void iSeries_end_IRQ(unsigned int irq)
338 {
339 }
340
341 static hw_irq_controller iSeries_IRQ_handler = {
342         .typename = "iSeries irq controller",
343         .startup = iSeries_startup_IRQ,
344         .shutdown = iSeries_shutdown_IRQ,
345         .enable = iSeries_enable_IRQ,
346         .disable = iSeries_disable_IRQ,
347         .end = iSeries_end_IRQ
348 };
349
350 /*
351  * This is called out of iSeries_scan_slot to allocate an IRQ for an EADS slot
352  * It calculates the irq value for the slot.
353  * Note that subBusNumber is always 0 (at the moment at least).
354  */
355 int __init iSeries_allocate_IRQ(HvBusNumber busNumber,
356                 HvSubBusNumber subBusNumber, HvAgentId deviceId)
357 {
358         unsigned int realirq, virtirq;
359         u8 idsel = (deviceId >> 4);
360         u8 function = deviceId & 7;
361
362         virtirq = next_virtual_irq++;
363         realirq = ((busNumber - 1) << 6) + ((idsel - 1) << 3) + function;
364         virt_irq_to_real_map[virtirq] = realirq;
365
366         irq_desc[virtirq].handler = &iSeries_IRQ_handler;
367         return virtirq;
368 }