irq_ia64.c 7.3 KB
Newer Older
L
Linus Torvalds 已提交
1
/*
2
 * linux/arch/ia64/kernel/irq_ia64.c
L
Linus Torvalds 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
 *
 * Copyright (C) 1998-2001 Hewlett-Packard Co
 *	Stephane Eranian <eranian@hpl.hp.com>
 *	David Mosberger-Tang <davidm@hpl.hp.com>
 *
 *  6/10/99: Updated to bring in sync with x86 version to facilitate
 *	     support for SMP and different interrupt controllers.
 *
 * 09/15/00 Goutham Rao <goutham.rao@intel.com> Implemented pci_irq_to_vector
 *                      PCI to vector allocation routine.
 * 04/14/2004 Ashok Raj <ashok.raj@intel.com>
 *						Added CPU Hotplug handling for IPF.
 */

#include <linux/module.h>

#include <linux/jiffies.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/kernel_stat.h>
#include <linux/slab.h>
#include <linux/ptrace.h>
#include <linux/random.h>	/* for rand_initialize_irq() */
#include <linux/signal.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/threads.h>
#include <linux/bitops.h>
33
#include <linux/irq.h>
L
Linus Torvalds 已提交
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48

#include <asm/delay.h>
#include <asm/intrinsics.h>
#include <asm/io.h>
#include <asm/hw_irq.h>
#include <asm/machvec.h>
#include <asm/pgtable.h>
#include <asm/system.h>

#ifdef CONFIG_PERFMON
# include <asm/perfmon.h>
#endif

#define IRQ_DEBUG	0

49 50 51 52
/* These can be overridden in platform_irq_init */
int ia64_first_device_vector = IA64_DEF_FIRST_DEVICE_VECTOR;
int ia64_last_device_vector = IA64_DEF_LAST_DEVICE_VECTOR;

L
Linus Torvalds 已提交
53 54 55 56 57 58 59 60 61 62 63 64 65 66
/* default base addr of IPI table */
void __iomem *ipi_base_addr = ((void __iomem *)
			       (__IA64_UNCACHED_OFFSET | IA64_IPI_DEFAULT_BASE_ADDR));

/*
 * Legacy IRQ to IA-64 vector translation table.
 */
__u8 isa_irq_to_vector_map[16] = {
	/* 8259 IRQ translation, first 16 entries */
	0x2f, 0x20, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29,
	0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21
};
EXPORT_SYMBOL(isa_irq_to_vector_map);

67
static unsigned long ia64_vector_mask[BITS_TO_LONGS(IA64_MAX_DEVICE_VECTORS)];
L
Linus Torvalds 已提交
68 69

int
70
assign_irq_vector (int irq)
L
Linus Torvalds 已提交
71 72 73 74 75 76
{
	int pos, vector;
 again:
	pos = find_first_zero_bit(ia64_vector_mask, IA64_NUM_DEVICE_VECTORS);
	vector = IA64_FIRST_DEVICE_VECTOR + pos;
	if (vector > IA64_LAST_DEVICE_VECTOR)
77
		return -ENOSPC;
L
Linus Torvalds 已提交
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
	if (test_and_set_bit(pos, ia64_vector_mask))
		goto again;
	return vector;
}

void
free_irq_vector (int vector)
{
	int pos;

	if (vector < IA64_FIRST_DEVICE_VECTOR || vector > IA64_LAST_DEVICE_VECTOR)
		return;

	pos = vector - IA64_FIRST_DEVICE_VECTOR;
	if (!test_and_clear_bit(pos, ia64_vector_mask))
		printk(KERN_WARNING "%s: double free!\n", __FUNCTION__);
}

96 97 98 99 100 101 102 103 104 105 106 107 108
int
reserve_irq_vector (int vector)
{
	int pos;

	if (vector < IA64_FIRST_DEVICE_VECTOR ||
	    vector > IA64_LAST_DEVICE_VECTOR)
		return -EINVAL;

	pos = vector - IA64_FIRST_DEVICE_VECTOR;
	return test_and_set_bit(pos, ia64_vector_mask);
}

109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
/*
 * Dynamic irq allocate and deallocation for MSI
 */
int create_irq(void)
{
	int vector = assign_irq_vector(AUTO_ASSIGN);

	if (vector >= 0)
		dynamic_irq_init(vector);

	return vector;
}

void destroy_irq(unsigned int irq)
{
	dynamic_irq_cleanup(irq);
	free_irq_vector(irq);
}

L
Linus Torvalds 已提交
128 129 130 131 132 133 134 135 136 137 138 139 140
#ifdef CONFIG_SMP
#	define IS_RESCHEDULE(vec)	(vec == IA64_IPI_RESCHEDULE)
#else
#	define IS_RESCHEDULE(vec)	(0)
#endif
/*
 * That's where the IVT branches when we get an external
 * interrupt. This branches to the correct hardware IRQ handler via
 * function ptr.
 */
void
ia64_handle_irq (ia64_vector vector, struct pt_regs *regs)
{
141
	struct pt_regs *old_regs = set_irq_regs(regs);
L
Linus Torvalds 已提交
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
	unsigned long saved_tpr;

#if IRQ_DEBUG
	{
		unsigned long bsp, sp;

		/*
		 * Note: if the interrupt happened while executing in
		 * the context switch routine (ia64_switch_to), we may
		 * get a spurious stack overflow here.  This is
		 * because the register and the memory stack are not
		 * switched atomically.
		 */
		bsp = ia64_getreg(_IA64_REG_AR_BSP);
		sp = ia64_getreg(_IA64_REG_SP);

		if ((sp - bsp) < 1024) {
			static unsigned char count;
			static long last_time;

			if (jiffies - last_time > 5*HZ)
				count = 0;
			if (++count < 5) {
				last_time = jiffies;
				printk("ia64_handle_irq: DANGER: less than "
				       "1KB of free stack space!!\n"
				       "(bsp=0x%lx, sp=%lx)\n", bsp, sp);
			}
		}
	}
#endif /* IRQ_DEBUG */

	/*
	 * Always set TPR to limit maximum interrupt nesting depth to
	 * 16 (without this, it would be ~240, which could easily lead
	 * to kernel stack overflows).
	 */
	irq_enter();
	saved_tpr = ia64_getreg(_IA64_REG_CR_TPR);
	ia64_srlz_d();
	while (vector != IA64_SPURIOUS_INT_VECTOR) {
		if (!IS_RESCHEDULE(vector)) {
			ia64_setreg(_IA64_REG_CR_TPR, vector);
			ia64_srlz_d();

187
			__do_IRQ(local_vector_to_irq(vector));
L
Linus Torvalds 已提交
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203

			/*
			 * Disable interrupts and send EOI:
			 */
			local_irq_disable();
			ia64_setreg(_IA64_REG_CR_TPR, saved_tpr);
		}
		ia64_eoi();
		vector = ia64_get_ivr();
	}
	/*
	 * This must be done *after* the ia64_eoi().  For example, the keyboard softirq
	 * handler needs to be able to wait for further keyboard interrupts, which can't
	 * come through until ia64_eoi() has been done.
	 */
	irq_exit();
204
	set_irq_regs(old_regs);
L
Linus Torvalds 已提交
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
}

#ifdef CONFIG_HOTPLUG_CPU
/*
 * This function emulates a interrupt processing when a cpu is about to be
 * brought down.
 */
void ia64_process_pending_intr(void)
{
	ia64_vector vector;
	unsigned long saved_tpr;
	extern unsigned int vectors_in_migration[NR_IRQS];

	vector = ia64_get_ivr();

	 irq_enter();
	 saved_tpr = ia64_getreg(_IA64_REG_CR_TPR);
	 ia64_srlz_d();

	 /*
	  * Perform normal interrupt style processing
	  */
	while (vector != IA64_SPURIOUS_INT_VECTOR) {
		if (!IS_RESCHEDULE(vector)) {
T
Tony Luck 已提交
229 230
			struct pt_regs *old_regs = set_irq_regs(NULL);

L
Linus Torvalds 已提交
231 232 233 234 235 236 237 238 239 240
			ia64_setreg(_IA64_REG_CR_TPR, vector);
			ia64_srlz_d();

			/*
			 * Now try calling normal ia64_handle_irq as it would have got called
			 * from a real intr handler. Try passing null for pt_regs, hopefully
			 * it will work. I hope it works!.
			 * Probably could shared code.
			 */
			vectors_in_migration[local_vector_to_irq(vector)]=0;
T
Tony Luck 已提交
241 242
			__do_IRQ(local_vector_to_irq(vector));
			set_irq_regs(old_regs);
L
Linus Torvalds 已提交
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258

			/*
			 * Disable interrupts and send EOI
			 */
			local_irq_disable();
			ia64_setreg(_IA64_REG_CR_TPR, saved_tpr);
		}
		ia64_eoi();
		vector = ia64_get_ivr();
	}
	irq_exit();
}
#endif


#ifdef CONFIG_SMP
T
Tony Luck 已提交
259
extern irqreturn_t handle_IPI (int irq, void *dev_id);
L
Linus Torvalds 已提交
260 261 262

static struct irqaction ipi_irqaction = {
	.handler =	handle_IPI,
263
	.flags =	IRQF_DISABLED,
L
Linus Torvalds 已提交
264 265 266 267 268 269 270 271 272 273 274 275
	.name =		"IPI"
};
#endif

void
register_percpu_irq (ia64_vector vec, struct irqaction *action)
{
	irq_desc_t *desc;
	unsigned int irq;

	for (irq = 0; irq < NR_IRQS; ++irq)
		if (irq_to_vector(irq) == vec) {
276
			desc = irq_desc + irq;
L
Linus Torvalds 已提交
277
			desc->status |= IRQ_PER_CPU;
278
			desc->chip = &irq_type_ia64_lsapic;
L
Linus Torvalds 已提交
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
			if (action)
				setup_irq(irq, action);
		}
}

void __init
init_IRQ (void)
{
	register_percpu_irq(IA64_SPURIOUS_INT_VECTOR, NULL);
#ifdef CONFIG_SMP
	register_percpu_irq(IA64_IPI_VECTOR, &ipi_irqaction);
#endif
#ifdef CONFIG_PERFMON
	pfm_init_percpu();
#endif
	platform_irq_init();
}

void
ia64_send_ipi (int cpu, int vector, int delivery_mode, int redirect)
{
	void __iomem *ipi_addr;
	unsigned long ipi_data;
	unsigned long phys_cpu_id;

#ifdef CONFIG_SMP
	phys_cpu_id = cpu_physical_id(cpu);
#else
	phys_cpu_id = (ia64_getreg(_IA64_REG_CR_LID) >> 16) & 0xffff;
#endif

	/*
	 * cpu number is in 8bit ID and 8bit EID
	 */

	ipi_data = (delivery_mode << 8) | (vector & 0xff);
	ipi_addr = ipi_base_addr + ((phys_cpu_id << 4) | ((redirect & 1) << 3));

	writeq(ipi_data, ipi_addr);
}