diff options
Diffstat (limited to 'arch/powerpc/platforms/iseries/irq.c')
-rw-r--r-- | arch/powerpc/platforms/iseries/irq.c | 400 |
1 files changed, 0 insertions, 400 deletions
diff --git a/arch/powerpc/platforms/iseries/irq.c b/arch/powerpc/platforms/iseries/irq.c deleted file mode 100644 index b210345..0000000 --- a/arch/powerpc/platforms/iseries/irq.c +++ /dev/null @@ -1,400 +0,0 @@ -/* - * This module supports the iSeries PCI bus interrupt handling - * Copyright (C) 20yy <Robert L Holtorf> <IBM Corp> - * Copyright (C) 2004-2005 IBM Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the: - * Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, - * Boston, MA 02111-1307 USA - * - * Change Activity: - * Created, December 13, 2000 by Wayne Holm - * End Change Activity - */ -#include <linux/pci.h> -#include <linux/init.h> -#include <linux/threads.h> -#include <linux/smp.h> -#include <linux/param.h> -#include <linux/string.h> -#include <linux/bootmem.h> -#include <linux/irq.h> -#include <linux/spinlock.h> - -#include <asm/paca.h> -#include <asm/iseries/hv_types.h> -#include <asm/iseries/hv_lp_event.h> -#include <asm/iseries/hv_call_xm.h> -#include <asm/iseries/it_lp_queue.h> - -#include "irq.h" -#include "pci.h" -#include "call_pci.h" - -#ifdef CONFIG_PCI - -enum pci_event_type { - pe_bus_created = 0, /* PHB has been created */ - pe_bus_error = 1, /* PHB has failed */ - pe_bus_failed = 2, /* Msg to Secondary, Primary failed bus */ - pe_node_failed = 4, /* Multi-adapter bridge has failed */ - pe_node_recovered = 5, /* Multi-adapter bridge has recovered */ - pe_bus_recovered = 12, /* PHB has been recovered */ - pe_unquiese_bus = 18, /* Secondary bus unqiescing */ - pe_bridge_error = 21, /* Bridge Error */ - pe_slot_interrupt = 22 /* Slot interrupt */ -}; - -struct pci_event { - struct HvLpEvent event; - union { - u64 __align; /* Align on an 8-byte boundary */ - struct { - u32 fisr; - HvBusNumber bus_number; - HvSubBusNumber sub_bus_number; - HvAgentId dev_id; - } slot; - struct { - HvBusNumber bus_number; - HvSubBusNumber sub_bus_number; - } bus; - struct { - HvBusNumber bus_number; - HvSubBusNumber sub_bus_number; - HvAgentId dev_id; - } node; - } data; -}; - -static DEFINE_SPINLOCK(pending_irqs_lock); -static int num_pending_irqs; -static int pending_irqs[NR_IRQS]; - -static void int_received(struct pci_event *event) -{ - int irq; - - switch (event->event.xSubtype) { - case pe_slot_interrupt: - irq = event->event.xCorrelationToken; - if (irq < NR_IRQS) { - spin_lock(&pending_irqs_lock); - pending_irqs[irq]++; - num_pending_irqs++; - spin_unlock(&pending_irqs_lock); - } else { - printk(KERN_WARNING "int_received: bad irq number %d\n", - irq); - HvCallPci_eoi(event->data.slot.bus_number, - event->data.slot.sub_bus_number, - event->data.slot.dev_id); - } - break; - /* Ignore error recovery events for now */ - case pe_bus_created: - printk(KERN_INFO "int_received: system bus %d created\n", - event->data.bus.bus_number); - break; - case pe_bus_error: - case pe_bus_failed: - printk(KERN_INFO "int_received: system bus %d failed\n", - event->data.bus.bus_number); - break; - case pe_bus_recovered: - case pe_unquiese_bus: - printk(KERN_INFO "int_received: system bus %d recovered\n", - event->data.bus.bus_number); - break; - case pe_node_failed: - case pe_bridge_error: - printk(KERN_INFO - "int_received: multi-adapter bridge %d/%d/%d failed\n", - event->data.node.bus_number, - event->data.node.sub_bus_number, - event->data.node.dev_id); - break; - case pe_node_recovered: - printk(KERN_INFO - "int_received: multi-adapter bridge %d/%d/%d recovered\n", - event->data.node.bus_number, - event->data.node.sub_bus_number, - event->data.node.dev_id); - break; - default: - printk(KERN_ERR - "int_received: unrecognized event subtype 0x%x\n", - event->event.xSubtype); - break; - } -} - -static void pci_event_handler(struct HvLpEvent *event) -{ - if (event && (event->xType == HvLpEvent_Type_PciIo)) { - if (hvlpevent_is_int(event)) - int_received((struct pci_event *)event); - else - printk(KERN_ERR - "pci_event_handler: unexpected ack received\n"); - } else if (event) - printk(KERN_ERR - "pci_event_handler: Unrecognized PCI event type 0x%x\n", - (int)event->xType); - else - printk(KERN_ERR "pci_event_handler: NULL event received\n"); -} - -#define REAL_IRQ_TO_SUBBUS(irq) (((irq) >> 14) & 0xff) -#define REAL_IRQ_TO_BUS(irq) ((((irq) >> 6) & 0xff) + 1) -#define REAL_IRQ_TO_IDSEL(irq) ((((irq) >> 3) & 7) + 1) -#define REAL_IRQ_TO_FUNC(irq) ((irq) & 7) - -/* - * This will be called by device drivers (via enable_IRQ) - * to enable INTA in the bridge interrupt status register. - */ -static void iseries_enable_IRQ(struct irq_data *d) -{ - u32 bus, dev_id, function, mask; - const u32 sub_bus = 0; - unsigned int rirq = (unsigned int)irqd_to_hwirq(d); - - /* The IRQ has already been locked by the caller */ - bus = REAL_IRQ_TO_BUS(rirq); - function = REAL_IRQ_TO_FUNC(rirq); - dev_id = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function; - - /* Unmask secondary INTA */ - mask = 0x80000000; - HvCallPci_unmaskInterrupts(bus, sub_bus, dev_id, mask); -} - -/* This is called by iseries_activate_IRQs */ -static unsigned int iseries_startup_IRQ(struct irq_data *d) -{ - u32 bus, dev_id, function, mask; - const u32 sub_bus = 0; - unsigned int rirq = (unsigned int)irqd_to_hwirq(d); - - bus = REAL_IRQ_TO_BUS(rirq); - function = REAL_IRQ_TO_FUNC(rirq); - dev_id = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function; - - /* Link the IRQ number to the bridge */ - HvCallXm_connectBusUnit(bus, sub_bus, dev_id, d->irq); - - /* Unmask bridge interrupts in the FISR */ - mask = 0x01010000 << function; - HvCallPci_unmaskFisr(bus, sub_bus, dev_id, mask); - iseries_enable_IRQ(d); - return 0; -} - -/* - * This is called out of iSeries_fixup to activate interrupt - * generation for usable slots - */ -void __init iSeries_activate_IRQs() -{ - int irq; - unsigned long flags; - - for_each_irq (irq) { - struct irq_desc *desc = irq_to_desc(irq); - struct irq_chip *chip; - - if (!desc) - continue; - - chip = irq_desc_get_chip(desc); - if (chip && chip->irq_startup) { - raw_spin_lock_irqsave(&desc->lock, flags); - chip->irq_startup(&desc->irq_data); - raw_spin_unlock_irqrestore(&desc->lock, flags); - } - } -} - -/* this is not called anywhere currently */ -static void iseries_shutdown_IRQ(struct irq_data *d) -{ - u32 bus, dev_id, function, mask; - const u32 sub_bus = 0; - unsigned int rirq = (unsigned int)irqd_to_hwirq(d); - - /* irq should be locked by the caller */ - bus = REAL_IRQ_TO_BUS(rirq); - function = REAL_IRQ_TO_FUNC(rirq); - dev_id = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function; - - /* Invalidate the IRQ number in the bridge */ - HvCallXm_connectBusUnit(bus, sub_bus, dev_id, 0); - - /* Mask bridge interrupts in the FISR */ - mask = 0x01010000 << function; - HvCallPci_maskFisr(bus, sub_bus, dev_id, mask); -} - -/* - * This will be called by device drivers (via disable_IRQ) - * to disable INTA in the bridge interrupt status register. - */ -static void iseries_disable_IRQ(struct irq_data *d) -{ - u32 bus, dev_id, function, mask; - const u32 sub_bus = 0; - unsigned int rirq = (unsigned int)irqd_to_hwirq(d); - - /* The IRQ has already been locked by the caller */ - bus = REAL_IRQ_TO_BUS(rirq); - function = REAL_IRQ_TO_FUNC(rirq); - dev_id = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function; - - /* Mask secondary INTA */ - mask = 0x80000000; - HvCallPci_maskInterrupts(bus, sub_bus, dev_id, mask); -} - -static void iseries_end_IRQ(struct irq_data *d) -{ - unsigned int rirq = (unsigned int)irqd_to_hwirq(d); - - HvCallPci_eoi(REAL_IRQ_TO_BUS(rirq), REAL_IRQ_TO_SUBBUS(rirq), - (REAL_IRQ_TO_IDSEL(rirq) << 4) + REAL_IRQ_TO_FUNC(rirq)); -} - -static struct irq_chip iseries_pic = { - .name = "iSeries", - .irq_startup = iseries_startup_IRQ, - .irq_shutdown = iseries_shutdown_IRQ, - .irq_unmask = iseries_enable_IRQ, - .irq_mask = iseries_disable_IRQ, - .irq_eoi = iseries_end_IRQ -}; - -/* - * This is called out of iSeries_scan_slot to allocate an IRQ for an EADS slot - * It calculates the irq value for the slot. - * Note that sub_bus is always 0 (at the moment at least). - */ -int __init iSeries_allocate_IRQ(HvBusNumber bus, - HvSubBusNumber sub_bus, u32 bsubbus) -{ - unsigned int realirq; - u8 idsel = ISERIES_GET_DEVICE_FROM_SUBBUS(bsubbus); - u8 function = ISERIES_GET_FUNCTION_FROM_SUBBUS(bsubbus); - - realirq = (((((sub_bus << 8) + (bus - 1)) << 3) + (idsel - 1)) << 3) - + function; - - return irq_create_mapping(NULL, realirq); -} - -#endif /* CONFIG_PCI */ - -/* - * Get the next pending IRQ. - */ -unsigned int iSeries_get_irq(void) -{ - int irq = NO_IRQ_IGNORE; - -#ifdef CONFIG_SMP - if (get_lppaca()->int_dword.fields.ipi_cnt) { - get_lppaca()->int_dword.fields.ipi_cnt = 0; - smp_ipi_demux(); - } -#endif /* CONFIG_SMP */ - if (hvlpevent_is_pending()) - process_hvlpevents(); - -#ifdef CONFIG_PCI - if (num_pending_irqs) { - spin_lock(&pending_irqs_lock); - for (irq = 0; irq < NR_IRQS; irq++) { - if (pending_irqs[irq]) { - pending_irqs[irq]--; - num_pending_irqs--; - break; - } - } - spin_unlock(&pending_irqs_lock); - if (irq >= NR_IRQS) - irq = NO_IRQ_IGNORE; - } -#endif - - return irq; -} - -#ifdef CONFIG_PCI - -static int iseries_irq_host_map(struct irq_host *h, unsigned int virq, - irq_hw_number_t hw) -{ - irq_set_chip_and_handler(virq, &iseries_pic, handle_fasteoi_irq); - - return 0; -} - -static int iseries_irq_host_match(struct irq_host *h, struct device_node *np) -{ - /* Match all */ - return 1; -} - -static struct irq_host_ops iseries_irq_host_ops = { - .map = iseries_irq_host_map, - .match = iseries_irq_host_match, -}; - -/* - * This is called by init_IRQ. set in ppc_md.init_IRQ by iSeries_setup.c - * It must be called before the bus walk. - */ -void __init iSeries_init_IRQ(void) -{ - /* Register PCI event handler and open an event path */ - struct irq_host *host; - int ret; - - /* - * The Hypervisor only allows us up to 256 interrupt - * sources (the irq number is passed in a u8). - */ - irq_set_virq_count(256); - - /* Create irq host. No need for a revmap since HV will give us - * back our virtual irq number - */ - host = irq_alloc_host(NULL, IRQ_HOST_MAP_NOMAP, 0, - &iseries_irq_host_ops, 0); - BUG_ON(host == NULL); - irq_set_default_host(host); - - ret = HvLpEvent_registerHandler(HvLpEvent_Type_PciIo, - &pci_event_handler); - if (ret == 0) { - ret = HvLpEvent_openPath(HvLpEvent_Type_PciIo, 0); - if (ret != 0) - printk(KERN_ERR "iseries_init_IRQ: open event path " - "failed with rc 0x%x\n", ret); - } else - printk(KERN_ERR "iseries_init_IRQ: register handler " - "failed with rc 0x%x\n", ret); -} - -#endif /* CONFIG_PCI */ |