File: /usr/src/linux/arch/ia64/kernel/irq_ia64.c

1     /*
2      * linux/arch/ia64/kernel/irq.c
3      *
4      * Copyright (C) 1998-2000 Hewlett-Packard Co
5      * Copyright (C) 1998, 1999 Stephane Eranian <eranian@hpl.hp.com>
6      * Copyright (C) 1999-2000 David Mosberger-Tang <davidm@hpl.hp.com>
7      *
8      *  6/10/99: Updated to bring in sync with x86 version to facilitate
9      *	     support for SMP and different interrupt controllers.
10      *
11      * 09/15/00 Goutham Rao <goutham.rao@intel.com> Implemented pci_irq_to_vector
12      *                      PCI to vector allocation routine.
13      */
14     
15     #include <linux/config.h>
16     
17     #include <linux/sched.h>
18     #include <linux/errno.h>
19     #include <linux/init.h>
20     #include <linux/interrupt.h>
21     #include <linux/ioport.h>
22     #include <linux/kernel_stat.h>
23     #include <linux/slab.h>
24     #include <linux/ptrace.h>
25     #include <linux/random.h>	/* for rand_initialize_irq() */
26     #include <linux/signal.h>
27     #include <linux/smp.h>
28     #include <linux/smp_lock.h>
29     #include <linux/threads.h>
30     
31     #include <asm/bitops.h>
32     #include <asm/delay.h>
33     #include <asm/io.h>
34     #include <asm/hw_irq.h>
35     #include <asm/machvec.h>
36     #include <asm/pgtable.h>
37     #include <asm/system.h>
38     
39     #define IRQ_DEBUG	0
40     
41     /* default base addr of IPI table */
42     unsigned long ipi_base_addr = (__IA64_UNCACHED_OFFSET | IA64_IPI_DEFAULT_BASE_ADDR);
43     
44     /*
45      * Legacy IRQ to IA-64 vector translation table.
46      */
47     __u8 isa_irq_to_vector_map[16] = {
48     	/* 8259 IRQ translation, first 16 entries */
49     	0x2f, 0x20, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29,
50     	0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21
51     };
52     
53     int
54     ia64_alloc_irq (void)
55     {
56     	static int next_irq = IA64_FIRST_DEVICE_VECTOR;
57     
58     	if (next_irq > IA64_LAST_DEVICE_VECTOR)
59     		/* XXX could look for sharable vectors instead of panic'ing... */
60     		panic("ia64_alloc_irq: out of interrupt vectors!");
61     	return next_irq++;
62     }
63     
64     extern unsigned int do_IRQ(unsigned long irq, struct pt_regs *regs);
65     
66     /*
67      * That's where the IVT branches when we get an external
68      * interrupt. This branches to the correct hardware IRQ handler via
69      * function ptr.
70      */
71     void
72     ia64_handle_irq (ia64_vector vector, struct pt_regs *regs)
73     {
74     	unsigned long saved_tpr;
75     #ifdef CONFIG_SMP
76     #	define IS_RESCHEDULE(vec)	(vec == IA64_IPI_RESCHEDULE)
77     #else
78     #	define IS_RESCHEDULE(vec)	(0)
79     #endif
80     
81     #if IRQ_DEBUG
82     	{
83     		unsigned long bsp, sp;
84     
85     		/*
86     		 * Note: if the interrupt happened while executing in
87     		 * the context switch routine (ia64_switch_to), we may
88     		 * get a spurious stack overflow here.  This is
89     		 * because the register and the memory stack are not
90     		 * switched atomically.
91     		 */
92     		asm ("mov %0=ar.bsp" : "=r"(bsp));
93     		asm ("mov %0=sp" : "=r"(sp));
94     
95     		if ((sp - bsp) < 1024) {
96     			static unsigned char count;
97     			static long last_time;
98     
99     			if (jiffies - last_time > 5*HZ)
100     				count = 0;
101     			if (++count < 5) {
102     				last_time = jiffies;
103     				printk("ia64_handle_irq: DANGER: less than "
104     				       "1KB of free stack space!!\n"
105     				       "(bsp=0x%lx, sp=%lx)\n", bsp, sp);
106     			}
107     		}
108     	}
109     #endif /* IRQ_DEBUG */
110     
111     	/*
112     	 * Always set TPR to limit maximum interrupt nesting depth to
113     	 * 16 (without this, it would be ~240, which could easily lead
114     	 * to kernel stack overflows).
115     	 */
116     	saved_tpr = ia64_get_tpr();
117     	ia64_srlz_d();
118     	while (vector != IA64_SPURIOUS_INT_VECTOR) {
119     		if (!IS_RESCHEDULE(vector)) {
120     			ia64_set_tpr(vector);
121     			ia64_srlz_d();
122     
123     			do_IRQ(local_vector_to_irq(vector), regs);
124     
125     			/*
126     			 * Disable interrupts and send EOI:
127     			 */
128     			local_irq_disable();
129     			ia64_set_tpr(saved_tpr);
130     		}
131     		ia64_eoi();
132     		vector = ia64_get_ivr();
133     	}
134     }
135     
136     #ifdef CONFIG_SMP
137     extern void handle_IPI (int irq, void *dev_id, struct pt_regs *regs);
138     
139     static struct irqaction ipi_irqaction = {
140     	handler:	handle_IPI,
141     	flags:		SA_INTERRUPT,
142     	name:		"IPI"
143     };
144     #endif
145     
146     void
147     register_percpu_irq (ia64_vector vec, struct irqaction *action)
148     {
149     	irq_desc_t *desc;
150     	unsigned int irq;
151     
152     	for (irq = 0; irq < NR_IRQS; ++irq)
153     		if (irq_to_vector(irq) == vec) {
154     			desc = irq_desc(irq);
155     			desc->status |= IRQ_PER_CPU;
156     			desc->handler = &irq_type_ia64_lsapic;
157     			if (action)
158     				setup_irq(irq, action);
159     		}
160     }
161     
162     void __init
163     init_IRQ (void)
164     {
165     	register_percpu_irq(IA64_SPURIOUS_INT_VECTOR, NULL);
166     #ifdef CONFIG_SMP
167     	register_percpu_irq(IA64_IPI_VECTOR, &ipi_irqaction);
168     #endif
169     	platform_irq_init();
170     }
171     
172     void
173     ia64_send_ipi (int cpu, int vector, int delivery_mode, int redirect)
174     {
175     	unsigned long ipi_addr;
176     	unsigned long ipi_data;
177     	unsigned long phys_cpu_id;
178     
179     #ifdef CONFIG_SMP
180     	phys_cpu_id = cpu_physical_id(cpu);
181     #else
182     	phys_cpu_id = (ia64_get_lid() >> 16) & 0xffff;
183     #endif
184     
185     	/*
186     	 * cpu number is in 8bit ID and 8bit EID
187     	 */
188     
189     	ipi_data = (delivery_mode << 8) | (vector & 0xff);
190     	ipi_addr = ipi_base_addr | (phys_cpu_id << 4) | ((redirect & 1)  << 3);
191     
192     	writeq(ipi_data, ipi_addr);
193     }
194