blob: cc6a266e7662d305503c05ad5777ef2f4c19c235 [file] [log] [blame]
Stephen Rothwell0c3b4f12005-06-21 17:15:49 -07001/*
2 * This module supports the iSeries PCI bus interrupt handling
3 * Copyright (C) 20yy <Robert L Holtorf> <IBM Corp>
Stephen Rothwell89ef68f2005-06-21 17:15:50 -07004 * Copyright (C) 2004-2005 IBM Corporation
Stephen Rothwell0c3b4f12005-06-21 17:15:49 -07005 *
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 */
Stephen Rothwell89ef68f2005-06-21 17:15:50 -070026#include <linux/config.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070027#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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070035#include <linux/irq.h>
36#include <linux/spinlock.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070037
Kelly Daly1ec65d72005-11-02 13:46:07 +110038#include <asm/iseries/hv_types.h>
Kelly Dalye45423e2005-11-02 12:08:31 +110039#include <asm/iseries/hv_lp_event.h>
Kelly Daly8021b8a2005-11-02 11:41:12 +110040#include <asm/iseries/hv_call_xm.h>
Stephen Rothwellb08567cb2005-09-28 23:37:01 +100041
42#include "irq.h"
Stephen Rothwellc6d2ea92005-10-14 17:16:17 +100043#include "call_pci.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070044
Stephen Rothwell60798c6a2005-11-16 17:47:43 +110045enum pci_event_type {
46 pe_bus_created = 0, /* PHB has been created */
47 pe_bus_error = 1, /* PHB has failed */
48 pe_bus_failed = 2, /* Msg to Secondary, Primary failed bus */
49 pe_node_failed = 4, /* Multi-adapter bridge has failed */
50 pe_node_recovered = 5, /* Multi-adapter bridge has recovered */
51 pe_bus_recovered = 12, /* PHB has been recovered */
52 pe_unquiese_bus = 18, /* Secondary bus unqiescing */
53 pe_bridge_error = 21, /* Bridge Error */
54 pe_slot_interrupt = 22 /* Slot interrupt */
Stephen Rothwell89ef68f2005-06-21 17:15:50 -070055};
56
Stephen Rothwell60798c6a2005-11-16 17:47:43 +110057struct pci_event {
58 struct HvLpEvent event;
Stephen Rothwell89ef68f2005-06-21 17:15:50 -070059 union {
Stephen Rothwell60798c6a2005-11-16 17:47:43 +110060 u64 __align; /* Align on an 8-byte boundary */
Stephen Rothwell89ef68f2005-06-21 17:15:50 -070061 struct {
62 u32 fisr;
Stephen Rothwell60798c6a2005-11-16 17:47:43 +110063 HvBusNumber bus_number;
64 HvSubBusNumber sub_bus_number;
65 HvAgentId dev_id;
66 } slot;
67 struct {
68 HvBusNumber bus_number;
69 HvSubBusNumber sub_bus_number;
70 } bus;
71 struct {
72 HvBusNumber bus_number;
73 HvSubBusNumber sub_bus_number;
74 HvAgentId dev_id;
75 } node;
76 } data;
Stephen Rothwell89ef68f2005-06-21 17:15:50 -070077};
78
Stephen Rothwell60798c6a2005-11-16 17:47:43 +110079static void int_received(struct pci_event *event, struct pt_regs *regs)
Stephen Rothwell89ef68f2005-06-21 17:15:50 -070080{
81 int irq;
Stephen Rothwell5a7b3ff2005-11-09 15:07:16 +110082#ifdef CONFIG_IRQSTACKS
83 struct thread_info *curtp, *irqtp;
84#endif
Stephen Rothwell89ef68f2005-06-21 17:15:50 -070085
Stephen Rothwell60798c6a2005-11-16 17:47:43 +110086 switch (event->event.xSubtype) {
87 case pe_slot_interrupt:
88 irq = event->event.xCorrelationToken;
Stephen Rothwell89ef68f2005-06-21 17:15:50 -070089 /* Dispatch the interrupt handlers for this irq */
Stephen Rothwell5a7b3ff2005-11-09 15:07:16 +110090#ifdef CONFIG_IRQSTACKS
91 /* Switch to the irq stack to handle this */
92 curtp = current_thread_info();
93 irqtp = hardirq_ctx[smp_processor_id()];
94 if (curtp != irqtp) {
95 irqtp->task = curtp->task;
96 irqtp->flags = 0;
Stephen Rothwell60798c6a2005-11-16 17:47:43 +110097 call___do_IRQ(irq, regs, irqtp);
Stephen Rothwell5a7b3ff2005-11-09 15:07:16 +110098 irqtp->task = NULL;
99 if (irqtp->flags)
100 set_bits(irqtp->flags, &curtp->flags);
101 } else
102#endif
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100103 __do_IRQ(irq, regs);
104 HvCallPci_eoi(event->data.slot.bus_number,
105 event->data.slot.sub_bus_number,
106 event->data.slot.dev_id);
Stephen Rothwell89ef68f2005-06-21 17:15:50 -0700107 break;
108 /* Ignore error recovery events for now */
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100109 case pe_bus_created:
110 printk(KERN_INFO "int_received: system bus %d created\n",
111 event->data.bus.bus_number);
Stephen Rothwell89ef68f2005-06-21 17:15:50 -0700112 break;
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100113 case pe_bus_error:
114 case pe_bus_failed:
115 printk(KERN_INFO "int_received: system bus %d failed\n",
116 event->data.bus.bus_number);
Stephen Rothwell89ef68f2005-06-21 17:15:50 -0700117 break;
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100118 case pe_bus_recovered:
119 case pe_unquiese_bus:
120 printk(KERN_INFO "int_received: system bus %d recovered\n",
121 event->data.bus.bus_number);
Stephen Rothwell89ef68f2005-06-21 17:15:50 -0700122 break;
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100123 case pe_node_failed:
124 case pe_bridge_error:
Stephen Rothwell89ef68f2005-06-21 17:15:50 -0700125 printk(KERN_INFO
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100126 "int_received: multi-adapter bridge %d/%d/%d failed\n",
127 event->data.node.bus_number,
128 event->data.node.sub_bus_number,
129 event->data.node.dev_id);
Stephen Rothwell89ef68f2005-06-21 17:15:50 -0700130 break;
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100131 case pe_node_recovered:
Stephen Rothwell89ef68f2005-06-21 17:15:50 -0700132 printk(KERN_INFO
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100133 "int_received: multi-adapter bridge %d/%d/%d recovered\n",
134 event->data.node.bus_number,
135 event->data.node.sub_bus_number,
136 event->data.node.dev_id);
Stephen Rothwell89ef68f2005-06-21 17:15:50 -0700137 break;
138 default:
139 printk(KERN_ERR
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100140 "int_received: unrecognized event subtype 0x%x\n",
141 event->event.xSubtype);
Stephen Rothwell89ef68f2005-06-21 17:15:50 -0700142 break;
143 }
144}
145
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100146static void pci_event_handler(struct HvLpEvent *event, struct pt_regs *regs)
Stephen Rothwell89ef68f2005-06-21 17:15:50 -0700147{
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100148 if (event && (event->xType == HvLpEvent_Type_PciIo)) {
149 switch (event->xFlags.xFunction) {
Stephen Rothwell89ef68f2005-06-21 17:15:50 -0700150 case HvLpEvent_Function_Int:
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100151 int_received((struct pci_event *)event, regs);
Stephen Rothwell89ef68f2005-06-21 17:15:50 -0700152 break;
153 case HvLpEvent_Function_Ack:
154 printk(KERN_ERR
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100155 "pci_event_handler: unexpected ack received\n");
Stephen Rothwell89ef68f2005-06-21 17:15:50 -0700156 break;
157 default:
158 printk(KERN_ERR
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100159 "pci_event_handler: unexpected event function %d\n",
160 (int)event->xFlags.xFunction);
Stephen Rothwell89ef68f2005-06-21 17:15:50 -0700161 break;
162 }
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100163 } else if (event)
Stephen Rothwell89ef68f2005-06-21 17:15:50 -0700164 printk(KERN_ERR
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100165 "pci_event_handler: Unrecognized PCI event type 0x%x\n",
166 (int)event->xType);
Stephen Rothwell89ef68f2005-06-21 17:15:50 -0700167 else
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100168 printk(KERN_ERR "pci_event_handler: NULL event received\n");
Stephen Rothwell89ef68f2005-06-21 17:15:50 -0700169}
170
Stephen Rothwell7f74e792005-06-21 17:15:51 -0700171/*
172 * This is called by init_IRQ. set in ppc_md.init_IRQ by iSeries_setup.c
173 * It must be called before the bus walk.
174 */
175void __init iSeries_init_IRQ(void)
Stephen Rothwell89ef68f2005-06-21 17:15:50 -0700176{
Stephen Rothwell7f74e792005-06-21 17:15:51 -0700177 /* Register PCI event handler and open an event path */
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100178 int ret;
Stephen Rothwell89ef68f2005-06-21 17:15:50 -0700179
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100180 ret = HvLpEvent_registerHandler(HvLpEvent_Type_PciIo,
181 &pci_event_handler);
182 if (ret == 0) {
183 ret = HvLpEvent_openPath(HvLpEvent_Type_PciIo, 0);
184 if (ret != 0)
185 printk(KERN_ERR "iseries_init_IRQ: open event path "
186 "failed with rc 0x%x\n", ret);
Stephen Rothwell89ef68f2005-06-21 17:15:50 -0700187 } else
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100188 printk(KERN_ERR "iseries_init_IRQ: register handler "
189 "failed with rc 0x%x\n", ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190}
191
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192#define REAL_IRQ_TO_BUS(irq) ((((irq) >> 6) & 0xff) + 1)
193#define REAL_IRQ_TO_IDSEL(irq) ((((irq) >> 3) & 7) + 1)
194#define REAL_IRQ_TO_FUNC(irq) ((irq) & 7)
195
Stephen Rothwell0c3b4f12005-06-21 17:15:49 -0700196/*
197 * This will be called by device drivers (via enable_IRQ)
198 * to enable INTA in the bridge interrupt status register.
199 */
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100200static void iseries_enable_IRQ(unsigned int irq)
Stephen Rothwell0c3b4f12005-06-21 17:15:49 -0700201{
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100202 u32 bus, dev_id, function, mask;
203 const u32 sub_bus = 0;
Stephen Rothwell0c3b4f12005-06-21 17:15:49 -0700204 unsigned int rirq = virt_irq_to_real_map[irq];
205
206 /* The IRQ has already been locked by the caller */
207 bus = REAL_IRQ_TO_BUS(rirq);
208 function = REAL_IRQ_TO_FUNC(rirq);
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100209 dev_id = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function;
Stephen Rothwell0c3b4f12005-06-21 17:15:49 -0700210
211 /* Unmask secondary INTA */
212 mask = 0x80000000;
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100213 HvCallPci_unmaskInterrupts(bus, sub_bus, dev_id, mask);
Stephen Rothwell0c3b4f12005-06-21 17:15:49 -0700214}
215
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100216/* This is called by iseries_activate_IRQs */
217static unsigned int iseries_startup_IRQ(unsigned int irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218{
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100219 u32 bus, dev_id, function, mask;
220 const u32 sub_bus = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 unsigned int rirq = virt_irq_to_real_map[irq];
222
223 bus = REAL_IRQ_TO_BUS(rirq);
224 function = REAL_IRQ_TO_FUNC(rirq);
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100225 dev_id = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226
227 /* Link the IRQ number to the bridge */
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100228 HvCallXm_connectBusUnit(bus, sub_bus, dev_id, irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229
230 /* Unmask bridge interrupts in the FISR */
231 mask = 0x01010000 << function;
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100232 HvCallPci_unmaskFisr(bus, sub_bus, dev_id, mask);
233 iseries_enable_IRQ(irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 return 0;
235}
236
237/*
238 * This is called out of iSeries_fixup to activate interrupt
239 * generation for usable slots
240 */
241void __init iSeries_activate_IRQs()
242{
243 int irq;
244 unsigned long flags;
245
246 for_each_irq (irq) {
247 irq_desc_t *desc = get_irq_desc(irq);
248
249 if (desc && desc->handler && desc->handler->startup) {
250 spin_lock_irqsave(&desc->lock, flags);
251 desc->handler->startup(irq);
252 spin_unlock_irqrestore(&desc->lock, flags);
253 }
Stephen Rothwell0c3b4f12005-06-21 17:15:49 -0700254 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255}
256
257/* this is not called anywhere currently */
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100258static void iseries_shutdown_IRQ(unsigned int irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259{
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100260 u32 bus, dev_id, function, mask;
261 const u32 sub_bus = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 unsigned int rirq = virt_irq_to_real_map[irq];
263
264 /* irq should be locked by the caller */
265 bus = REAL_IRQ_TO_BUS(rirq);
266 function = REAL_IRQ_TO_FUNC(rirq);
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100267 dev_id = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268
269 /* Invalidate the IRQ number in the bridge */
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100270 HvCallXm_connectBusUnit(bus, sub_bus, dev_id, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271
272 /* Mask bridge interrupts in the FISR */
273 mask = 0x01010000 << function;
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100274 HvCallPci_maskFisr(bus, sub_bus, dev_id, mask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275}
276
277/*
278 * This will be called by device drivers (via disable_IRQ)
279 * to disable INTA in the bridge interrupt status register.
280 */
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100281static void iseries_disable_IRQ(unsigned int irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282{
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100283 u32 bus, dev_id, function, mask;
284 const u32 sub_bus = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 unsigned int rirq = virt_irq_to_real_map[irq];
286
287 /* The IRQ has already been locked by the caller */
288 bus = REAL_IRQ_TO_BUS(rirq);
289 function = REAL_IRQ_TO_FUNC(rirq);
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100290 dev_id = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291
292 /* Mask secondary INTA */
293 mask = 0x80000000;
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100294 HvCallPci_maskInterrupts(bus, sub_bus, dev_id, mask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295}
296
297/*
Stephen Rothwelld4be4f32005-11-09 16:19:53 +1100298 * This does nothing because there is not enough information
299 * provided to do the EOI HvCall. This is done by XmPciLpEvent.c
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 */
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100301static void iseries_end_IRQ(unsigned int irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302{
303}
Stephen Rothwell0c3b4f12005-06-21 17:15:49 -0700304
305static hw_irq_controller iSeries_IRQ_handler = {
306 .typename = "iSeries irq controller",
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100307 .startup = iseries_startup_IRQ,
308 .shutdown = iseries_shutdown_IRQ,
309 .enable = iseries_enable_IRQ,
310 .disable = iseries_disable_IRQ,
311 .end = iseries_end_IRQ
Stephen Rothwell0c3b4f12005-06-21 17:15:49 -0700312};
313
314/*
315 * This is called out of iSeries_scan_slot to allocate an IRQ for an EADS slot
316 * It calculates the irq value for the slot.
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100317 * Note that sub_bus_number is always 0 (at the moment at least).
Stephen Rothwell0c3b4f12005-06-21 17:15:49 -0700318 */
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100319int __init iSeries_allocate_IRQ(HvBusNumber bus_number,
320 HvSubBusNumber sub_bus_number, HvAgentId dev_id)
Stephen Rothwell0c3b4f12005-06-21 17:15:49 -0700321{
Stephen Rothwelld9ae2ba2005-11-10 18:11:19 +1100322 int virtirq;
323 unsigned int realirq;
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100324 u8 idsel = (dev_id >> 4);
325 u8 function = dev_id & 7;
Stephen Rothwell0c3b4f12005-06-21 17:15:49 -0700326
Stephen Rothwell60798c6a2005-11-16 17:47:43 +1100327 realirq = ((bus_number - 1) << 6) + ((idsel - 1) << 3) + function;
Stephen Rothwelld9ae2ba2005-11-10 18:11:19 +1100328 virtirq = virt_irq_create_mapping(realirq);
Stephen Rothwell0c3b4f12005-06-21 17:15:49 -0700329
330 irq_desc[virtirq].handler = &iSeries_IRQ_handler;
331 return virtirq;
332}