Linux preempt-rt

Check our new training course

Real-Time Linux with PREEMPT_RT

Check our new training course
with Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

  1
  2
  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
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
/*
 *	linux/arch/alpha/kernel/irq.c
 *
 *	Copyright (C) 1995 Linus Torvalds
 *
 * This file contains the code used by various IRQ handling routines:
 * asking for different IRQ's should be done through these routines
 * instead of just grabbing them. Thus setups with different IRQ numbers
 * shouldn't result in any weird surprises, and installing new handlers
 * should be easier.
 */

#include <linux/config.h>
#include <linux/ptrace.h>
#include <linux/errno.h>
#include <linux/kernel_stat.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>

#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/bitops.h>

static unsigned char cache_21 = 0xff;
static unsigned char cache_A1 = 0xff;

void disable_irq(unsigned int irq_nr)
{
	unsigned long flags;
	unsigned char mask;

	mask = 1 << (irq_nr & 7);
	save_flags(flags);
	if (irq_nr < 8) {
		cli();
		cache_21 |= mask;
		outb(cache_21,0x21);
		restore_flags(flags);
		return;
	}
	cli();
	cache_A1 |= mask;
	outb(cache_A1,0xA1);
	restore_flags(flags);
}

void enable_irq(unsigned int irq_nr)
{
	unsigned long flags;
	unsigned char mask;

	mask = ~(1 << (irq_nr & 7));
	save_flags(flags);
	if (irq_nr < 8) {
		cli();
		cache_21 &= mask;
		outb(cache_21,0x21);
		restore_flags(flags);
		return;
	}
	cli();
	cache_A1 &= mask;
	outb(cache_A1,0xA1);
	restore_flags(flags);
}

/*
 * Initial irq handlers.
 */
struct irqaction {
	void (*handler)(int, struct pt_regs *);
	unsigned long flags;
	unsigned long mask;
	const char *name;
};

static struct irqaction irq_action[16] = {
	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }
};

int get_irq_list(char *buf)
{
	int i, len = 0;
	struct irqaction * action = irq_action;

	for (i = 0 ; i < 16 ; i++, action++) {
		if (!action->handler)
			continue;
		len += sprintf(buf+len, "%2d: %8d %c %s\n",
			i, kstat.interrupts[i],
			(action->flags & SA_INTERRUPT) ? '+' : ' ',
			action->name);
	}
	return len;
}

static inline void ack_irq(int irq)
{
	/* ACK the interrupt making it the lowest priority */
	/*  First the slave .. */
	if (irq > 7) {
		outb(0xE0 | (irq - 8), 0xa0);
		irq = 2;
	}
	/* .. then the master */
	outb(0xE0 | irq, 0x20);
}

static inline void mask_irq(int irq)
{
	if (irq < 8) {
		cache_21 |= 1 << irq;
		outb(cache_21, 0x21);
	} else {
		cache_A1 |= 1 << (irq - 8);
		outb(cache_A1, 0xA1);
	}
}

static inline void unmask_irq(unsigned long irq)
{
	if (irq < 8) {
		cache_21 &= ~(1 << irq);
		outb(cache_21, 0x21);
	} else {
		cache_A1 &= ~(1 << (irq - 8));
		outb(cache_A1, 0xA1);
	}
}

int request_irq(unsigned int irq, void (*handler)(int, struct pt_regs *),
	unsigned long irqflags, const char * devname)
{
	struct irqaction * action;
	unsigned long flags;

	if (irq > 15)
		return -EINVAL;
	action = irq + irq_action;
	if (action->handler)
		return -EBUSY;
	if (!handler)
		return -EINVAL;
	save_flags(flags);
	cli();
	action->handler = handler;
	action->flags = irqflags;
	action->mask = 0;
	action->name = devname;
	if (irq < 8 && irq) {
		cache_21 &= ~(1<<irq);
		outb(cache_21,0x21);
	} else {
		cache_21 &= ~(1<<2);
		cache_A1 &= ~(1<<(irq-8));
		outb(cache_21,0x21);
		outb(cache_A1,0xA1);
	}
	restore_flags(flags);
	return 0;
}

void free_irq(unsigned int irq)
{
	struct irqaction * action = irq + irq_action;
	unsigned long flags;

	if (irq > 15) {
		printk("Trying to free IRQ%d\n", irq);
		return;
	}
	if (!action->handler) {
		printk("Trying to free free IRQ%d\n", irq);
		return;
	}
	save_flags(flags);
	cli();
	mask_irq(irq);
	action->handler = NULL;
	action->flags = 0;
	action->mask = 0;
	action->name = NULL;
	restore_flags(flags);
}

static void handle_nmi(struct pt_regs * regs)
{
	printk("Whee.. NMI received. Probable hardware error\n");
	printk("61=%02x, 461=%02x\n", inb(0x61), inb(0x461));
}

static void unexpected_irq(int irq, struct pt_regs * regs)
{
	int i;

	printk("IO device interrupt, irq = %d\n", irq);
	printk("PC = %016lx PS=%04lx\n", regs->pc, regs->ps);
	printk("Expecting: ");
	for (i = 0; i < 16; i++)
		if (irq_action[i].handler)
			printk("[%s:%d] ", irq_action[i].name, i);
	printk("\n");
	printk("64=%02x, 60=%02x, 3fa=%02x 2fa=%02x\n",
		inb(0x64), inb(0x60), inb(0x3fa), inb(0x2fa));
	outb(0x0c, 0x3fc);
	outb(0x0c, 0x2fc);
	outb(0,0x61);
	outb(0,0x461);
}

static inline void handle_irq(int irq, struct pt_regs * regs)
{
	struct irqaction * action = irq + irq_action;

	kstat.interrupts[irq]++;
	if (!action->handler) {
		unexpected_irq(irq, regs);
		return;
	}
	action->handler(irq, regs);
}

static void local_device_interrupt(unsigned long vector, struct pt_regs * regs)
{
	switch (vector) {
		/* com1: map to irq 4 */
		case 0x900:
			handle_irq(4, regs);
			return;

		/* com2: map to irq 3 */
		case 0x920:
			handle_irq(3, regs);
			return;

		/* keyboard: map to irq 1 */
		case 0x980:
			handle_irq(1, regs);
			return;

		/* mouse: map to irq 12 */
		case 0x990:
			handle_irq(12, regs);
			return;
		default:
			printk("Unknown local interrupt %lx\n", vector);
	}
}

/*
 * The vector is 0x8X0 for EISA interrupt X, and 0x9X0 for the local
 * motherboard interrupts.. This is for the Jensen.
 *
 *	0x660 - NMI
 *
 *	0x800 - IRQ0  interval timer (not used, as we use the RTC timer)
 *	0x810 - IRQ1  line printer (duh..)
 *	0x860 - IRQ6  floppy disk
 *	0x8E0 - IRQ14 SCSI controller
 *
 *	0x900 - COM1
 *	0x920 - COM2
 *	0x980 - keyboard
 *	0x990 - mouse
 *
 * The PCI version is more sane: it doesn't have the local interrupts at
 * all, and has only normal PCI interrupts from devices. Happily it's easy
 * enough to do a sane mapping from the Jensen.. Note that this means
 * that we may have to do a hardware "ack" to a different interrupt than
 * we report to the rest of the world..
 */
static void device_interrupt(unsigned long vector, struct pt_regs * regs)
{
	int irq, ack;
	struct irqaction * action;

	if (vector == 0x660) {
		handle_nmi(regs);
		return;
	}

	ack = irq = (vector - 0x800) >> 4;
#ifndef CONFIG_PCI
	if (vector >= 0x900) {
		local_device_interrupt(vector, regs);
		return;
	}
	/* irq1 is supposed to be the keyboard, silly Jensen */
	if (irq == 1)
		irq = 7;
#endif
	printk("%d%d", irq, ack);
	kstat.interrupts[irq]++;
	action = irq_action + irq;
	/* quick interrupts get executed with no extra overhead */
	if (action->flags & SA_INTERRUPT) {
		action->handler(irq, regs);
		ack_irq(ack);
		return;
	}
	/*
	 * For normal interrupts, we mask it out, and then ACK it.
	 * This way another (more timing-critical) interrupt can
	 * come through while we're doing this one.
	 *
	 * Note! A irq without a handler gets masked and acked, but
	 * never unmasked. The autoirq stuff depends on this (it looks
	 * at the masks before and after doing the probing).
	 */
	mask_irq(ack);
	ack_irq(ack);
	if (!action->handler)
		return;
	action->handler(irq, regs);
	unmask_irq(ack);
}

/*
 * Start listening for interrupts..
 */
unsigned int probe_irq_on(void)
{
	unsigned int i, irqs = 0, irqmask;
	unsigned long delay;

	for (i = 15; i > 0; i--) {
		if (!irq_action[i].handler) {
			enable_irq(i);
			irqs |= (1 << i);
		}
	}

	/* wait for spurious interrupts to mask themselves out again */
	for (delay = jiffies + HZ/10; delay > jiffies; )
		/* about 100 ms delay */;
	
	/* now filter out any obviously spurious interrupts */
	irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int) cache_21;
	irqs &= ~irqmask;
	return irqs;
}

/*
 * Get the result of the IRQ probe.. A negative result means that
 * we have several candidates (but we return the lowest-numbered
 * one).
 */
int probe_irq_off(unsigned int irqs)
{
	unsigned int i, irqmask;
	
	irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int)cache_21;
	irqs &= irqmask;
	if (!irqs)
		return 0;
	i = ffz(~irqs);
	if (irqs != (1 << i))
		i = -i;
	return i;
}

static void machine_check(unsigned long vector, unsigned long la_ptr, struct pt_regs * regs)
{
	printk("Machine check\n");
}

asmlinkage void do_entInt(unsigned long type, unsigned long vector,
	unsigned long la_ptr, struct pt_regs *regs)
{
	switch (type) {
		case 0:
			printk("Interprocessor interrupt? You must be kidding\n");
			break;
		case 1:
			/* timer interrupt.. */
			handle_irq(0, regs);
			return;
		case 2:
			machine_check(vector, la_ptr, regs);
			break;
		case 3:
			device_interrupt(vector, regs);
			return;
		case 4:
			printk("Performance counter interrupt\n");
			break;;
		default:
			printk("Hardware intr %ld %lx? Huh?\n", type, vector);
	}
	printk("PC = %016lx PS=%04lx\n", regs->pc, regs->ps);
}

extern asmlinkage void entInt(void);

void init_IRQ(void)
{
	wrent(entInt, 0);
}