xref: /openbmc/linux/arch/m68k/q40/q40ints.c (revision 40202cca)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * arch/m68k/q40/q40ints.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Copyright (C) 1999,2001 Richard Zidlicky
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * This file is subject to the terms and conditions of the GNU General Public
71da177e4SLinus Torvalds  * License.  See the file COPYING in the main directory of this archive
81da177e4SLinus Torvalds  * for more details.
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  * .. used to be loosely based on bvme6000ints.c
111da177e4SLinus Torvalds  *
121da177e4SLinus Torvalds  */
131da177e4SLinus Torvalds 
141da177e4SLinus Torvalds #include <linux/types.h>
151da177e4SLinus Torvalds #include <linux/kernel.h>
161da177e4SLinus Torvalds #include <linux/errno.h>
171da177e4SLinus Torvalds #include <linux/interrupt.h>
18a03010edSGeert Uytterhoeven #include <linux/irq.h>
191da177e4SLinus Torvalds 
20d6444094SArnd Bergmann #include <asm/machdep.h>
211da177e4SLinus Torvalds #include <asm/ptrace.h>
221da177e4SLinus Torvalds #include <asm/traps.h>
231da177e4SLinus Torvalds 
241da177e4SLinus Torvalds #include <asm/q40_master.h>
251da177e4SLinus Torvalds #include <asm/q40ints.h>
261da177e4SLinus Torvalds 
271da177e4SLinus Torvalds /*
281da177e4SLinus Torvalds  * Q40 IRQs are defined as follows:
291da177e4SLinus Torvalds  *            3,4,5,6,7,10,11,14,15 : ISA dev IRQs
301da177e4SLinus Torvalds  *            16-31: reserved
311da177e4SLinus Torvalds  *            32   : keyboard int
321da177e4SLinus Torvalds  *            33   : frame int (50/200 Hz periodic timer)
331da177e4SLinus Torvalds  *            34   : sample int (10/20 KHz periodic timer)
341da177e4SLinus Torvalds  *
351da177e4SLinus Torvalds  */
361da177e4SLinus Torvalds 
3777dda339SRoman Zippel static void q40_irq_handler(unsigned int, struct pt_regs *fp);
38e8abf5e7SGeert Uytterhoeven static void q40_irq_enable(struct irq_data *data);
39e8abf5e7SGeert Uytterhoeven static void q40_irq_disable(struct irq_data *data);
401da177e4SLinus Torvalds 
4177dda339SRoman Zippel unsigned short q40_ablecount[35];
4277dda339SRoman Zippel unsigned short q40_state[35];
431da177e4SLinus Torvalds 
q40_irq_startup(struct irq_data * data)44e8abf5e7SGeert Uytterhoeven static unsigned int q40_irq_startup(struct irq_data *data)
4577dda339SRoman Zippel {
46e8abf5e7SGeert Uytterhoeven 	unsigned int irq = data->irq;
47e8abf5e7SGeert Uytterhoeven 
4877dda339SRoman Zippel 	/* test for ISA ints not implemented by HW */
4977dda339SRoman Zippel 	switch (irq) {
5077dda339SRoman Zippel 	case 1: case 2: case 8: case 9:
5177dda339SRoman Zippel 	case 11: case 12: case 13:
52446926f9SGeert Uytterhoeven 		pr_warn("%s: ISA IRQ %d not implemented by HW\n", __func__,
53446926f9SGeert Uytterhoeven 			irq);
54c288bf25SGeert Uytterhoeven 		/* FIXME return -ENXIO; */
5577dda339SRoman Zippel 	}
5677dda339SRoman Zippel 	return 0;
5777dda339SRoman Zippel }
581da177e4SLinus Torvalds 
q40_irq_shutdown(struct irq_data * data)59e8abf5e7SGeert Uytterhoeven static void q40_irq_shutdown(struct irq_data *data)
6077dda339SRoman Zippel {
6177dda339SRoman Zippel }
621da177e4SLinus Torvalds 
63c288bf25SGeert Uytterhoeven static struct irq_chip q40_irq_chip = {
6477dda339SRoman Zippel 	.name		= "q40",
65c288bf25SGeert Uytterhoeven 	.irq_startup	= q40_irq_startup,
66c288bf25SGeert Uytterhoeven 	.irq_shutdown	= q40_irq_shutdown,
67e8abf5e7SGeert Uytterhoeven 	.irq_enable	= q40_irq_enable,
68e8abf5e7SGeert Uytterhoeven 	.irq_disable	= q40_irq_disable,
6977dda339SRoman Zippel };
701da177e4SLinus Torvalds 
711da177e4SLinus Torvalds /*
721da177e4SLinus Torvalds  * void q40_init_IRQ (void)
731da177e4SLinus Torvalds  *
741da177e4SLinus Torvalds  * Parameters:	None
751da177e4SLinus Torvalds  *
761da177e4SLinus Torvalds  * Returns:	Nothing
771da177e4SLinus Torvalds  *
781da177e4SLinus Torvalds  * This function is called during kernel startup to initialize
791da177e4SLinus Torvalds  * the q40 IRQ handling routines.
801da177e4SLinus Torvalds  */
811da177e4SLinus Torvalds 
8277dda339SRoman Zippel static int disabled;
831da177e4SLinus Torvalds 
q40_init_IRQ(void)8466a3f820SAl Viro void __init q40_init_IRQ(void)
851da177e4SLinus Torvalds {
86edb34725SGeert Uytterhoeven 	m68k_setup_irq_controller(&q40_irq_chip, handle_simple_irq, 1,
87edb34725SGeert Uytterhoeven 				  Q40_IRQ_MAX);
881da177e4SLinus Torvalds 
891da177e4SLinus Torvalds 	/* setup handler for ISA ints */
9077dda339SRoman Zippel 	m68k_setup_auto_interrupt(q40_irq_handler);
9177dda339SRoman Zippel 
92e8abf5e7SGeert Uytterhoeven 	m68k_irq_startup_irq(IRQ_AUTO_2);
93e8abf5e7SGeert Uytterhoeven 	m68k_irq_startup_irq(IRQ_AUTO_4);
941da177e4SLinus Torvalds 
951da177e4SLinus Torvalds 	/* now enable some ints.. */
961da177e4SLinus Torvalds 	master_outb(1, EXT_ENABLE_REG);  /* ISA IRQ 5-15 */
971da177e4SLinus Torvalds 
981da177e4SLinus Torvalds 	/* make sure keyboard IRQ is disabled */
991da177e4SLinus Torvalds 	master_outb(0, KEY_IRQ_ENABLE_REG);
1001da177e4SLinus Torvalds }
1011da177e4SLinus Torvalds 
1021da177e4SLinus Torvalds 
1031da177e4SLinus Torvalds /*
1041da177e4SLinus Torvalds  * this stuff doesn't really belong here..
1051da177e4SLinus Torvalds  */
1061da177e4SLinus Torvalds 
1071da177e4SLinus Torvalds int ql_ticks;              /* 200Hz ticks since last jiffie */
1081da177e4SLinus Torvalds static int sound_ticks;
1091da177e4SLinus Torvalds 
1101da177e4SLinus Torvalds #define SVOL 45
1111da177e4SLinus Torvalds 
q40_mksound(unsigned int hz,unsigned int ticks)1121da177e4SLinus Torvalds void q40_mksound(unsigned int hz, unsigned int ticks)
1131da177e4SLinus Torvalds {
1141da177e4SLinus Torvalds 	/* for now ignore hz, except that hz==0 switches off sound */
1151da177e4SLinus Torvalds 	/* simply alternate the ampl (128-SVOL)-(128+SVOL)-..-.. at 200Hz */
11677dda339SRoman Zippel 	if (hz == 0) {
1171da177e4SLinus Torvalds 		if (sound_ticks)
1181da177e4SLinus Torvalds 			sound_ticks = 1;
1191da177e4SLinus Torvalds 
1201da177e4SLinus Torvalds 		*DAC_LEFT = 128;
1211da177e4SLinus Torvalds 		*DAC_RIGHT = 128;
1221da177e4SLinus Torvalds 
1231da177e4SLinus Torvalds 		return;
1241da177e4SLinus Torvalds 	}
1251da177e4SLinus Torvalds 	/* sound itself is done in q40_timer_int */
12677dda339SRoman Zippel 	if (sound_ticks == 0)
12777dda339SRoman Zippel 		sound_ticks = 1000; /* pretty long beep */
1281da177e4SLinus Torvalds 	sound_ticks = ticks << 1;
1291da177e4SLinus Torvalds }
1301da177e4SLinus Torvalds 
q40_timer_int(int irq,void * dev_id)1311efdd4bdSFinn Thain static irqreturn_t q40_timer_int(int irq, void *dev_id)
1321da177e4SLinus Torvalds {
1331da177e4SLinus Torvalds 	ql_ticks = ql_ticks ? 0 : 1;
13477dda339SRoman Zippel 	if (sound_ticks) {
1351da177e4SLinus Torvalds 		unsigned char sval=(sound_ticks & 1) ? 128-SVOL : 128+SVOL;
1361da177e4SLinus Torvalds 		sound_ticks--;
1371da177e4SLinus Torvalds 		*DAC_LEFT=sval;
1381da177e4SLinus Torvalds 		*DAC_RIGHT=sval;
1391da177e4SLinus Torvalds 	}
1401da177e4SLinus Torvalds 
1411efdd4bdSFinn Thain 	if (!ql_ticks) {
1421efdd4bdSFinn Thain 		unsigned long flags;
1431efdd4bdSFinn Thain 
1441efdd4bdSFinn Thain 		local_irq_save(flags);
14542f1d57fSArnd Bergmann 		legacy_timer_tick(1);
146d6444094SArnd Bergmann 		timer_heartbeat();
1471efdd4bdSFinn Thain 		local_irq_restore(flags);
1481efdd4bdSFinn Thain 	}
1491da177e4SLinus Torvalds 	return IRQ_HANDLED;
1501da177e4SLinus Torvalds }
1511da177e4SLinus Torvalds 
q40_sched_init(void)152f9a01539SArnd Bergmann void q40_sched_init (void)
1531da177e4SLinus Torvalds {
1541da177e4SLinus Torvalds 	int timer_irq;
1551da177e4SLinus Torvalds 
1561da177e4SLinus Torvalds 	timer_irq = Q40_IRQ_FRAME;
1571da177e4SLinus Torvalds 
15842f1d57fSArnd Bergmann 	if (request_irq(timer_irq, q40_timer_int, 0, "timer", NULL))
1591da177e4SLinus Torvalds 		panic("Couldn't register timer int");
1601da177e4SLinus Torvalds 
1611da177e4SLinus Torvalds 	master_outb(-1, FRAME_CLEAR_REG);
1621da177e4SLinus Torvalds 	master_outb( 1, FRAME_RATE_REG);
1631da177e4SLinus Torvalds }
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds 
1661da177e4SLinus Torvalds /*
1671da177e4SLinus Torvalds  * tables to translate bits into IRQ numbers
1681da177e4SLinus Torvalds  * it is a good idea to order the entries by priority
1691da177e4SLinus Torvalds  *
1701da177e4SLinus Torvalds */
1711da177e4SLinus Torvalds 
1721da177e4SLinus Torvalds struct IRQ_TABLE{ unsigned mask; int irq ;};
1731da177e4SLinus Torvalds #if 0
1741da177e4SLinus Torvalds static struct IRQ_TABLE iirqs[]={
1751da177e4SLinus Torvalds   {Q40_IRQ_FRAME_MASK,Q40_IRQ_FRAME},
1761da177e4SLinus Torvalds   {Q40_IRQ_KEYB_MASK,Q40_IRQ_KEYBOARD},
1771da177e4SLinus Torvalds   {0,0}};
1781da177e4SLinus Torvalds #endif
1791da177e4SLinus Torvalds static struct IRQ_TABLE eirqs[] = {
1801da177e4SLinus Torvalds   { .mask = Q40_IRQ3_MASK,	.irq = 3 },	/* ser 1 */
1811da177e4SLinus Torvalds   { .mask = Q40_IRQ4_MASK,	.irq = 4 },	/* ser 2 */
1821da177e4SLinus Torvalds   { .mask = Q40_IRQ14_MASK,	.irq = 14 },	/* IDE 1 */
1831da177e4SLinus Torvalds   { .mask = Q40_IRQ15_MASK,	.irq = 15 },	/* IDE 2 */
1841da177e4SLinus Torvalds   { .mask = Q40_IRQ6_MASK,	.irq = 6 },	/* floppy, handled elsewhere */
1851da177e4SLinus Torvalds   { .mask = Q40_IRQ7_MASK,	.irq = 7 },	/* par */
1861da177e4SLinus Torvalds   { .mask = Q40_IRQ5_MASK,	.irq = 5 },
1871da177e4SLinus Torvalds   { .mask = Q40_IRQ10_MASK,	.irq = 10 },
1881da177e4SLinus Torvalds   {0,0}
1891da177e4SLinus Torvalds };
1901da177e4SLinus Torvalds 
1911da177e4SLinus Torvalds /* complain only this many times about spurious ints : */
1920c79cf6aSSimon Arlott static int ccleirq=60;    /* ISA dev IRQs*/
1931da177e4SLinus Torvalds /*static int cclirq=60;*/     /* internal */
1941da177e4SLinus Torvalds 
1951da177e4SLinus Torvalds /* FIXME: add shared ints,mask,unmask,probing.... */
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds #define IRQ_INPROGRESS 1
1981da177e4SLinus Torvalds /*static unsigned short saved_mask;*/
1991da177e4SLinus Torvalds //static int do_tint=0;
2001da177e4SLinus Torvalds 
2011da177e4SLinus Torvalds #define DEBUG_Q40INT
2021da177e4SLinus Torvalds /*#define IP_USE_DISABLE *//* would be nice, but crashes ???? */
2031da177e4SLinus Torvalds 
204*40202ccaSXin Gao static int mext_disabled;	/* ext irq disabled by master chip? */
205*40202ccaSXin Gao static int aliased_irq;		/* how many times inside handler ?*/
2061da177e4SLinus Torvalds 
2071da177e4SLinus Torvalds 
20877dda339SRoman Zippel /* got interrupt, dispatch to ISA or keyboard/timer IRQs */
q40_irq_handler(unsigned int irq,struct pt_regs * fp)20977dda339SRoman Zippel static void q40_irq_handler(unsigned int irq, struct pt_regs *fp)
2101da177e4SLinus Torvalds {
2111da177e4SLinus Torvalds 	unsigned mir, mer;
21277dda339SRoman Zippel 	int i;
2131da177e4SLinus Torvalds 
2141da177e4SLinus Torvalds //repeat:
2151da177e4SLinus Torvalds 	mir = master_inb(IIRQ_REG);
21677dda339SRoman Zippel #ifdef CONFIG_BLK_DEV_FD
21777dda339SRoman Zippel 	if ((mir & Q40_IRQ_EXT_MASK) &&
21877dda339SRoman Zippel 	    (master_inb(EIRQ_REG) & Q40_IRQ6_MASK)) {
21977dda339SRoman Zippel 		floppy_hardint();
22077dda339SRoman Zippel 		return;
22177dda339SRoman Zippel 	}
22277dda339SRoman Zippel #endif
22377dda339SRoman Zippel 	switch (irq) {
22477dda339SRoman Zippel 	case 4:
22577dda339SRoman Zippel 	case 6:
2261425df87SGeert Uytterhoeven 		do_IRQ(Q40_IRQ_SAMPLE, fp);
22777dda339SRoman Zippel 		return;
22877dda339SRoman Zippel 	}
2291da177e4SLinus Torvalds 	if (mir & Q40_IRQ_FRAME_MASK) {
2301425df87SGeert Uytterhoeven 		do_IRQ(Q40_IRQ_FRAME, fp);
2311da177e4SLinus Torvalds 		master_outb(-1, FRAME_CLEAR_REG);
2321da177e4SLinus Torvalds 	}
2331da177e4SLinus Torvalds 	if ((mir & Q40_IRQ_SER_MASK) || (mir & Q40_IRQ_EXT_MASK)) {
2341da177e4SLinus Torvalds 		mer = master_inb(EIRQ_REG);
2351da177e4SLinus Torvalds 		for (i = 0; eirqs[i].mask; i++) {
23677dda339SRoman Zippel 			if (mer & eirqs[i].mask) {
2371da177e4SLinus Torvalds 				irq = eirqs[i].irq;
2381da177e4SLinus Torvalds /*
2391da177e4SLinus Torvalds  * There is a little mess wrt which IRQ really caused this irq request. The
2401da177e4SLinus Torvalds  * main problem is that IIRQ_REG and EIRQ_REG reflect the state when they
2411da177e4SLinus Torvalds  * are read - which is long after the request came in. In theory IRQs should
2420c79cf6aSSimon Arlott  * not just go away but they occasionally do
2431da177e4SLinus Torvalds  */
2441da177e4SLinus Torvalds 				if (irq > 4 && irq <= 15 && mext_disabled) {
2451da177e4SLinus Torvalds 					/*aliased_irq++;*/
2461da177e4SLinus Torvalds 					goto iirq;
2471da177e4SLinus Torvalds 				}
24877dda339SRoman Zippel 				if (q40_state[irq] & IRQ_INPROGRESS) {
2491da177e4SLinus Torvalds 					/* some handlers do local_irq_enable() for irq latency reasons, */
2501da177e4SLinus Torvalds 					/* however reentering an active irq handler is not permitted */
2511da177e4SLinus Torvalds #ifdef IP_USE_DISABLE
2521da177e4SLinus Torvalds 					/* in theory this is the better way to do it because it still */
2531da177e4SLinus Torvalds 					/* lets through eg the serial irqs, unfortunately it crashes */
2541da177e4SLinus Torvalds 					disable_irq(irq);
2551da177e4SLinus Torvalds 					disabled = 1;
2561da177e4SLinus Torvalds #else
257446926f9SGeert Uytterhoeven 					/*pr_warn("IRQ_INPROGRESS detected for irq %d, disabling - %s disabled\n",
25877dda339SRoman Zippel 						irq, disabled ? "already" : "not yet"); */
2591da177e4SLinus Torvalds 					fp->sr = (((fp->sr) & (~0x700))+0x200);
2601da177e4SLinus Torvalds 					disabled = 1;
2611da177e4SLinus Torvalds #endif
2621da177e4SLinus Torvalds 					goto iirq;
2631da177e4SLinus Torvalds 				}
26477dda339SRoman Zippel 				q40_state[irq] |= IRQ_INPROGRESS;
2651425df87SGeert Uytterhoeven 				do_IRQ(irq, fp);
26677dda339SRoman Zippel 				q40_state[irq] &= ~IRQ_INPROGRESS;
2671da177e4SLinus Torvalds 
2681da177e4SLinus Torvalds 				/* naively enable everything, if that fails than    */
2691da177e4SLinus Torvalds 				/* this function will be reentered immediately thus */
2701da177e4SLinus Torvalds 				/* getting another chance to disable the IRQ        */
2711da177e4SLinus Torvalds 
2721da177e4SLinus Torvalds 				if (disabled) {
2731da177e4SLinus Torvalds #ifdef IP_USE_DISABLE
2741da177e4SLinus Torvalds 					if (irq > 4) {
2751da177e4SLinus Torvalds 						disabled = 0;
27677dda339SRoman Zippel 						enable_irq(irq);
27777dda339SRoman Zippel 					}
2781da177e4SLinus Torvalds #else
2791da177e4SLinus Torvalds 					disabled = 0;
280446926f9SGeert Uytterhoeven 					/*pr_info("reenabling irq %d\n", irq); */
2811da177e4SLinus Torvalds #endif
2821da177e4SLinus Torvalds 				}
2831da177e4SLinus Torvalds // used to do 'goto repeat;' here, this delayed bh processing too long
28477dda339SRoman Zippel 				return;
2851da177e4SLinus Torvalds 			}
2861da177e4SLinus Torvalds 		}
28777dda339SRoman Zippel 		if (mer && ccleirq > 0 && !aliased_irq) {
288446926f9SGeert Uytterhoeven 			pr_warn("ISA interrupt from unknown source? EIRQ_REG = %x\n",
289446926f9SGeert Uytterhoeven 				mer);
29077dda339SRoman Zippel 			ccleirq--;
29177dda339SRoman Zippel 		}
2921da177e4SLinus Torvalds 	}
2931da177e4SLinus Torvalds  iirq:
2941da177e4SLinus Torvalds 	mir = master_inb(IIRQ_REG);
2951da177e4SLinus Torvalds 	/* should test whether keyboard irq is really enabled, doing it in defhand */
29677dda339SRoman Zippel 	if (mir & Q40_IRQ_KEYB_MASK)
2971425df87SGeert Uytterhoeven 		do_IRQ(Q40_IRQ_KEYBOARD, fp);
2981da177e4SLinus Torvalds 
29977dda339SRoman Zippel 	return;
3001da177e4SLinus Torvalds }
3011da177e4SLinus Torvalds 
q40_irq_enable(struct irq_data * data)302e8abf5e7SGeert Uytterhoeven void q40_irq_enable(struct irq_data *data)
3031da177e4SLinus Torvalds {
304e8abf5e7SGeert Uytterhoeven 	unsigned int irq = data->irq;
305e8abf5e7SGeert Uytterhoeven 
30677dda339SRoman Zippel 	if (irq >= 5 && irq <= 15) {
3071da177e4SLinus Torvalds 		mext_disabled--;
3081da177e4SLinus Torvalds 		if (mext_disabled > 0)
309446926f9SGeert Uytterhoeven 			pr_warn("q40_irq_enable : nested disable/enable\n");
3101da177e4SLinus Torvalds 		if (mext_disabled == 0)
3111da177e4SLinus Torvalds 			master_outb(1, EXT_ENABLE_REG);
3121da177e4SLinus Torvalds 	}
3131da177e4SLinus Torvalds }
3141da177e4SLinus Torvalds 
3151da177e4SLinus Torvalds 
q40_irq_disable(struct irq_data * data)316e8abf5e7SGeert Uytterhoeven void q40_irq_disable(struct irq_data *data)
3171da177e4SLinus Torvalds {
318e8abf5e7SGeert Uytterhoeven 	unsigned int irq = data->irq;
319e8abf5e7SGeert Uytterhoeven 
3201da177e4SLinus Torvalds 	/* disable ISA iqs : only do something if the driver has been
3211da177e4SLinus Torvalds 	 * verified to be Q40 "compatible" - right now IDE, NE2K
3221da177e4SLinus Torvalds 	 * Any driver should not attempt to sleep across disable_irq !!
3231da177e4SLinus Torvalds 	 */
3241da177e4SLinus Torvalds 
3251da177e4SLinus Torvalds 	if (irq >= 5 && irq <= 15) {
3261da177e4SLinus Torvalds 		master_outb(0, EXT_ENABLE_REG);
3271da177e4SLinus Torvalds 		mext_disabled++;
32877dda339SRoman Zippel 		if (mext_disabled > 1)
329446926f9SGeert Uytterhoeven 			pr_info("disable_irq nesting count %d\n",
330446926f9SGeert Uytterhoeven 				mext_disabled);
3311da177e4SLinus Torvalds 	}
3321da177e4SLinus Torvalds }
333