patch-2.4.20 linux-2.4.20/arch/mips64/sgi-ip27/ip27-irq.c
Next file: linux-2.4.20/arch/mips64/sgi-ip27/ip27-klconfig.c
Previous file: linux-2.4.20/arch/mips64/sgi-ip27/ip27-irq-glue.S
Back to the patch index
Back to the overall index
- Lines: 488
- Date:
Wed Dec 31 16:00:00 1969
- Orig file:
linux-2.4.19/arch/mips64/sgi-ip27/ip27-irq.c
- Orig date:
Fri Aug 2 17:39:43 2002
diff -urN linux-2.4.19/arch/mips64/sgi-ip27/ip27-irq.c linux-2.4.20/arch/mips64/sgi-ip27/ip27-irq.c
@@ -1,487 +0,0 @@
-/*
- * ip27-irq.c: Highlevel interrupt handling for IP27 architecture.
- *
- * Copyright (C) 1999, 2000 Ralf Baechle (ralf@gnu.org)
- * Copyright (C) 1999, 2000 Silicon Graphics, Inc.
- * Copyright (C) 1999 - 2001 Kanoj Sarcar
- */
-#include <linux/config.h>
-#include <linux/init.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/timex.h>
-#include <linux/slab.h>
-#include <linux/random.h>
-#include <linux/smp_lock.h>
-#include <linux/kernel_stat.h>
-#include <linux/delay.h>
-
-#include <asm/bitops.h>
-#include <asm/bootinfo.h>
-#include <asm/io.h>
-#include <asm/mipsregs.h>
-#include <asm/system.h>
-#include <asm/irq.h>
-
-#include <asm/ptrace.h>
-#include <asm/processor.h>
-#include <asm/pci/bridge.h>
-#include <asm/sn/sn0/hub.h>
-#include <asm/sn/sn0/ip27.h>
-#include <asm/sn/addrs.h>
-#include <asm/sn/agent.h>
-#include <asm/sn/arch.h>
-#include <asm/sn/intr.h>
-#include <asm/sn/intr_public.h>
-
-
-#undef DEBUG_IRQ
-#ifdef DEBUG_IRQ
-#define DBG(x...) printk(x)
-#else
-#define DBG(x...)
-#endif
-
-/* These should die */
-unsigned char bus_to_wid[256]; /* widget id for linux pci bus */
-unsigned char bus_to_nid[256]; /* nasid for linux pci bus */
-unsigned char num_bridges; /* number of bridges in the system */
-
-/*
- * Linux has a controller-independent x86 interrupt architecture.
- * every controller has a 'controller-template', that is used
- * by the main code to do the right thing. Each driver-visible
- * interrupt source is transparently wired to the apropriate
- * controller. Thus drivers need not be aware of the
- * interrupt-controller.
- *
- * Various interrupt controllers we handle: 8259 PIC, SMP IO-APIC,
- * PIIX4's internal 8259 PIC and SGI's Visual Workstation Cobalt (IO-)APIC.
- * (IO-APICs assumed to be messaging to Pentium local-APICs)
- *
- * the code is designed to be easily extended with new/different
- * interrupt controllers, without having to do assembly magic.
- */
-
-extern asmlinkage void ip27_irq(void);
-extern void do_IRQ(int irq, struct pt_regs *regs);
-
-extern int irq_to_bus[], irq_to_slot[], bus_to_cpu[];
-int intr_connect_level(int cpu, int bit);
-int intr_disconnect_level(int cpu, int bit);
-
-/*
- * There is a single intpend register per node, and we want to have
- * distinct levels for intercpu intrs for both cpus A and B on a node.
- */
-int node_level_to_irq[MAX_COMPACT_NODES][PERNODE_LEVELS];
-
-/*
- * use these macros to get the encoded nasid and widget id
- * from the irq value
- */
-#define IRQ_TO_BUS(i) irq_to_bus[(i)]
-#define IRQ_TO_CPU(i) bus_to_cpu[IRQ_TO_BUS(i)]
-#define NASID_FROM_PCI_IRQ(i) bus_to_nid[IRQ_TO_BUS(i)]
-#define WID_FROM_PCI_IRQ(i) bus_to_wid[IRQ_TO_BUS(i)]
-#define SLOT_FROM_PCI_IRQ(i) irq_to_slot[i]
-
-static inline int alloc_level(cpuid_t cpunum, int irq)
-{
- cnodeid_t nodenum = CPUID_TO_COMPACT_NODEID(cpunum);
- int j = LEAST_LEVEL + 3; /* resched & crosscall entries taken */
-
- while (++j < PERNODE_LEVELS) {
- if (node_level_to_irq[nodenum][j] == -1) {
- node_level_to_irq[nodenum][j] = irq;
- return j;
- }
- }
- printk("Cpu %ld flooded with devices\n", cpunum);
- while(1);
- return -1;
-}
-
-static inline int find_level(cpuid_t *cpunum, int irq)
-{
- int j;
- cnodeid_t nodenum = INVALID_CNODEID;
-
- while (++nodenum < MAX_COMPACT_NODES) {
- j = LEAST_LEVEL + 3; /* resched & crosscall entries taken */
- while (++j < PERNODE_LEVELS)
- if (node_level_to_irq[nodenum][j] == irq) {
- *cpunum = 0; /* XXX Fixme */
- return(j);
- }
- }
- printk("Could not identify cpu/level for irq %d\n", irq);
- while(1);
- return(-1);
-}
-
-/*
- * Find first bit set
- */
-static int ms1bit(unsigned long x)
-{
- int b = 0, s;
-
- s = 16; if (x >> 16 == 0) s = 0; b += s; x >>= s;
- s = 8; if (x >> 8 == 0) s = 0; b += s; x >>= s;
- s = 4; if (x >> 4 == 0) s = 0; b += s; x >>= s;
- s = 2; if (x >> 2 == 0) s = 0; b += s; x >>= s;
- s = 1; if (x >> 1 == 0) s = 0; b += s;
-
- return b;
-}
-
-/*
- * This code is unnecessarily complex, because we do SA_INTERRUPT
- * intr enabling. Basically, once we grab the set of intrs we need
- * to service, we must mask _all_ these interrupts; firstly, to make
- * sure the same intr does not intr again, causing recursion that
- * can lead to stack overflow. Secondly, we can not just mask the
- * one intr we are do_IRQing, because the non-masked intrs in the
- * first set might intr again, causing multiple servicings of the
- * same intr. This effect is mostly seen for intercpu intrs.
- * Kanoj 05.13.00
- */
-void ip27_do_irq(struct pt_regs *regs)
-{
- int irq, swlevel;
- hubreg_t pend0, mask0;
- cpuid_t thiscpu = smp_processor_id();
- int pi_int_mask0 = ((cputoslice(thiscpu) == 0) ?
- PI_INT_MASK0_A : PI_INT_MASK0_B);
-
- /* copied from Irix intpend0() */
- while (((pend0 = LOCAL_HUB_L(PI_INT_PEND0)) &
- (mask0 = LOCAL_HUB_L(pi_int_mask0))) != 0) {
- pend0 &= mask0; /* Pick intrs we should look at */
- if (pend0) {
- /* Prevent any of the picked intrs from recursing */
- LOCAL_HUB_S(pi_int_mask0, mask0 & ~(pend0));
- do {
- swlevel = ms1bit(pend0);
- LOCAL_HUB_CLR_INTR(swlevel);
- /* "map" swlevel to irq */
- irq = LEVEL_TO_IRQ(thiscpu, swlevel);
- do_IRQ(irq, regs);
- /* clear bit in pend0 */
- pend0 ^= 1ULL << swlevel;
- } while(pend0);
- /* Now allow the set of serviced intrs again */
- LOCAL_HUB_S(pi_int_mask0, mask0);
- LOCAL_HUB_L(PI_INT_PEND0);
- }
- }
-}
-
-
-/* Startup one of the (PCI ...) IRQs routes over a bridge. */
-static unsigned int startup_bridge_irq(unsigned int irq)
-{
- bridgereg_t device;
- bridge_t *bridge;
- int pin, swlevel;
- cpuid_t cpu;
- nasid_t master = NASID_FROM_PCI_IRQ(irq);
-
- if (irq < BASE_PCI_IRQ)
- return 0;
-
- bridge = (bridge_t *) NODE_SWIN_BASE(master, WID_FROM_PCI_IRQ(irq));
- pin = SLOT_FROM_PCI_IRQ(irq);
- cpu = IRQ_TO_CPU(irq);
-
- DBG("bridge_startup(): irq= 0x%x pin=%d\n", irq, pin);
- /*
- * "map" irq to a swlevel greater than 6 since the first 6 bits
- * of INT_PEND0 are taken
- */
- swlevel = alloc_level(cpu, irq);
- intr_connect_level(cpu, swlevel);
-
- bridge->b_int_addr[pin].addr = (0x20000 | swlevel | (master << 8));
- bridge->b_int_enable |= (1 << pin);
- /* more stuff in int_enable reg */
- bridge->b_int_enable |= 0x7ffffe00;
-
- /*
- * XXX This only works if b_int_device is initialized to 0!
- * We program the bridge to have a 1:1 mapping between devices
- * (slots) and intr pins.
- */
- device = bridge->b_int_device;
- device |= (pin << (pin*3));
- bridge->b_int_device = device;
-
- bridge->b_widget.w_tflush; /* Flush */
-
- return 0; /* Never anything pending. */
-}
-
-/* Shutdown one of the (PCI ...) IRQs routes over a bridge. */
-static unsigned int shutdown_bridge_irq(unsigned int irq)
-{
- bridge_t *bridge;
- int pin, swlevel;
- cpuid_t cpu;
-
- if (irq < BASE_PCI_IRQ)
- return 0;
-
- bridge = (bridge_t *) NODE_SWIN_BASE(NASID_FROM_PCI_IRQ(irq),
- WID_FROM_PCI_IRQ(irq));
- DBG("bridge_shutdown: irq 0x%x\n", irq);
- pin = SLOT_FROM_PCI_IRQ(irq);
-
- /*
- * map irq to a swlevel greater than 6 since the first 6 bits
- * of INT_PEND0 are taken
- */
- swlevel = find_level(&cpu, irq);
- intr_disconnect_level(cpu, swlevel);
- LEVEL_TO_IRQ(cpu, swlevel) = -1;
-
- bridge->b_int_enable &= ~(1 << pin);
- bridge->b_widget.w_tflush; /* Flush */
-
- return 0; /* Never anything pending. */
-}
-
-static inline void enable_bridge_irq(unsigned int irq)
-{
- /* All the braindamage happens magically for us in ip27_do_irq */
-}
-
-static void disable_bridge_irq(unsigned int irq)
-{
- /* All the braindamage happens magically for us in ip27_do_irq */
-}
-
-static void mask_and_ack_bridge_irq(unsigned int irq)
-{
- /* All the braindamage happens magically for us in ip27_do_irq */
-}
-
-static void end_bridge_irq (unsigned int irq)
-{
- if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
- enable_bridge_irq(irq);
-}
-
-static struct hw_interrupt_type bridge_irq_type = {
- "bridge",
- startup_bridge_irq,
- shutdown_bridge_irq,
- enable_bridge_irq,
- disable_bridge_irq,
- mask_and_ack_bridge_irq,
- end_bridge_irq
-};
-
-void irq_debug(void)
-{
- bridge_t *bridge = (bridge_t *) 0x9200000008000000;
-
- printk("bridge->b_int_status = 0x%x\n", bridge->b_int_status);
- printk("bridge->b_int_enable = 0x%x\n", bridge->b_int_enable);
- printk("PI_INT_PEND0 = 0x%lx\n", LOCAL_HUB_L(PI_INT_PEND0));
- printk("PI_INT_MASK0_A = 0x%lx\n", LOCAL_HUB_L(PI_INT_MASK0_A));
-}
-
-void __init init_IRQ(void)
-{
- int i;
-
- set_except_vector(0, ip27_irq);
-
- /*
- * Right now the bridge irq is our kitchen sink interrupt type
- */
- for (i = 0; i <= NR_IRQS; i++) {
- irq_desc[i].status = IRQ_DISABLED;
- irq_desc[i].action = 0;
- irq_desc[i].depth = 1;
- irq_desc[i].handler = &bridge_irq_type;
- }
-}
-
-/*
- * Get values that vary depending on which CPU and bit we're operating on.
- */
-static hub_intmasks_t *intr_get_ptrs(cpuid_t cpu, int bit, int *new_bit,
- hubreg_t **intpend_masks, int *ip)
-{
- hub_intmasks_t *hub_intmasks;
-
- hub_intmasks = &cpu_data[cpu].p_intmasks;
- if (bit < N_INTPEND_BITS) {
- *intpend_masks = hub_intmasks->intpend0_masks;
- *ip = 0;
- *new_bit = bit;
- } else {
- *intpend_masks = hub_intmasks->intpend1_masks;
- *ip = 1;
- *new_bit = bit - N_INTPEND_BITS;
- }
- return hub_intmasks;
-}
-
-int intr_connect_level(int cpu, int bit)
-{
- int ip;
- int slice = cputoslice(cpu);
- volatile hubreg_t *mask_reg;
- hubreg_t *intpend_masks;
- nasid_t nasid = COMPACT_TO_NASID_NODEID(cputocnode(cpu));
-
- (void)intr_get_ptrs(cpu, bit, &bit, &intpend_masks, &ip);
-
- /* Make sure it's not already pending when we connect it. */
- REMOTE_HUB_CLR_INTR(nasid, bit + ip * N_INTPEND_BITS);
-
- intpend_masks[0] |= (1ULL << (u64)bit);
-
- if (ip == 0) {
- mask_reg = REMOTE_HUB_ADDR(nasid, PI_INT_MASK0_A +
- PI_INT_MASK_OFFSET * slice);
- } else {
- mask_reg = REMOTE_HUB_ADDR(nasid, PI_INT_MASK1_A +
- PI_INT_MASK_OFFSET * slice);
- }
- HUB_S(mask_reg, intpend_masks[0]);
- return(0);
-}
-
-int intr_disconnect_level(int cpu, int bit)
-{
- int ip;
- int slice = cputoslice(cpu);
- volatile hubreg_t *mask_reg;
- hubreg_t *intpend_masks;
- nasid_t nasid = COMPACT_TO_NASID_NODEID(cputocnode(cpu));
-
- (void)intr_get_ptrs(cpu, bit, &bit, &intpend_masks, &ip);
- intpend_masks[0] &= ~(1ULL << (u64)bit);
- if (ip == 0) {
- mask_reg = REMOTE_HUB_ADDR(nasid, PI_INT_MASK0_A +
- PI_INT_MASK_OFFSET * slice);
- } else {
- mask_reg = REMOTE_HUB_ADDR(nasid, PI_INT_MASK1_A +
- PI_INT_MASK_OFFSET * slice);
- }
- HUB_S(mask_reg, intpend_masks[0]);
- return(0);
-}
-
-
-void handle_resched_intr(int irq, void *dev_id, struct pt_regs *regs)
-{
- /* Nothing, the return from intr will work for us */
-}
-
-#ifdef CONFIG_SMP
-
-void core_send_ipi(int destid, unsigned int action)
-{
- int irq;
-
-#if (CPUS_PER_NODE == 2)
- switch (action) {
- case SMP_RESCHEDULE_YOURSELF:
- irq = CPU_RESCHED_A_IRQ;
- break;
- case SMP_CALL_FUNCTION:
- irq = CPU_CALL_A_IRQ;
- break;
- default:
- panic("sendintr");
- }
- irq += cputoslice(destid);
-
- /*
- * Convert the compact hub number to the NASID to get the correct
- * part of the address space. Then set the interrupt bit associated
- * with the CPU we want to send the interrupt to.
- */
- REMOTE_HUB_SEND_INTR(COMPACT_TO_NASID_NODEID(cputocnode(destid)),
- FAST_IRQ_TO_LEVEL(irq));
-#else
- << Bomb! Must redefine this for more than 2 CPUS. >>
-#endif
-}
-
-#endif
-
-extern void smp_call_function_interrupt(void);
-
-void install_cpuintr(int cpu)
-{
-#ifdef CONFIG_SMP
-#if (CPUS_PER_NODE == 2)
- static int done = 0;
-
- /*
- * This is a hack till we have a pernode irqlist. Currently,
- * just have the master cpu set up the handlers for the per
- * cpu irqs.
- */
- if (done == 0) {
- int j;
-
- if (request_irq(CPU_RESCHED_A_IRQ, handle_resched_intr,
- 0, "resched", 0))
- panic("intercpu intr unconnectible");
- if (request_irq(CPU_RESCHED_B_IRQ, handle_resched_intr,
- 0, "resched", 0))
- panic("intercpu intr unconnectible");
- if (request_irq(CPU_CALL_A_IRQ, smp_call_function_interrupt,
- 0, "callfunc", 0))
- panic("intercpu intr unconnectible");
- if (request_irq(CPU_CALL_B_IRQ, smp_call_function_interrupt,
- 0, "callfunc", 0))
- panic("intercpu intr unconnectible");
-
- for (j = 0; j < PERNODE_LEVELS; j++)
- LEVEL_TO_IRQ(0, j) = -1;
- LEVEL_TO_IRQ(0, FAST_IRQ_TO_LEVEL(CPU_RESCHED_A_IRQ)) =
- CPU_RESCHED_A_IRQ;
- LEVEL_TO_IRQ(0, FAST_IRQ_TO_LEVEL(CPU_RESCHED_B_IRQ)) =
- CPU_RESCHED_B_IRQ;
- LEVEL_TO_IRQ(0, FAST_IRQ_TO_LEVEL(CPU_CALL_A_IRQ)) =
- CPU_CALL_A_IRQ;
- LEVEL_TO_IRQ(0, FAST_IRQ_TO_LEVEL(CPU_CALL_B_IRQ)) =
- CPU_CALL_B_IRQ;
- for (j = 1; j < MAX_COMPACT_NODES; j++)
- memcpy(&node_level_to_irq[j][0],
- &node_level_to_irq[0][0],
- sizeof(node_level_to_irq[0][0])*PERNODE_LEVELS);
-
- done = 1;
- }
-
- intr_connect_level(cpu, FAST_IRQ_TO_LEVEL(CPU_RESCHED_A_IRQ +
- cputoslice(cpu)));
- intr_connect_level(cpu, FAST_IRQ_TO_LEVEL(CPU_CALL_A_IRQ +
- cputoslice(cpu)));
-#else /* CPUS_PER_NODE */
-#error Must redefine this for more than 2 CPUS.
-#endif /* CPUS_PER_NODE */
-#endif /* CONFIG_SMP */
-}
-
-void install_tlbintr(int cpu)
-{
-#if 0
- int intr_bit = N_INTPEND_BITS + TLB_INTR_A + cputoslice(cpu);
-
- intr_connect_level(cpu, intr_bit);
-#endif
-}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)