xref: /openbmc/linux/arch/m68k/mac/oss.c (revision bd0b9ac405e1794d72533c3d487aa65b6b955a0c)
11da177e4SLinus Torvalds /*
2da3fb3c9SFinn Thain  *	Operating System Services (OSS) chip handling
31da177e4SLinus Torvalds  *	Written by Joshua M. Thompson (funaho@jurai.org)
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  *	This chip is used in the IIfx in place of VIA #2. It acts like a fancy
71da177e4SLinus Torvalds  *	VIA chip with prorammable interrupt levels.
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  * 990502 (jmt) - Major rewrite for new interrupt architecture as well as some
101da177e4SLinus Torvalds  *		  recent insights into OSS operational details.
110c79cf6aSSimon Arlott  * 990610 (jmt) - Now taking full advantage of the OSS. Interrupts are mapped
121da177e4SLinus Torvalds  *		  to mostly match the A/UX interrupt scheme supported on the
131da177e4SLinus Torvalds  *		  VIA side. Also added support for enabling the ISM irq again
141da177e4SLinus Torvalds  *		  since we now have a functional IOP manager.
151da177e4SLinus Torvalds  */
161da177e4SLinus Torvalds 
171da177e4SLinus Torvalds #include <linux/types.h>
181da177e4SLinus Torvalds #include <linux/kernel.h>
191da177e4SLinus Torvalds #include <linux/mm.h>
201da177e4SLinus Torvalds #include <linux/delay.h>
211da177e4SLinus Torvalds #include <linux/init.h>
22ddc7fd25SGeert Uytterhoeven #include <linux/irq.h>
231da177e4SLinus Torvalds 
241da177e4SLinus Torvalds #include <asm/macintosh.h>
251da177e4SLinus Torvalds #include <asm/macints.h>
261da177e4SLinus Torvalds #include <asm/mac_via.h>
271da177e4SLinus Torvalds #include <asm/mac_oss.h>
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds int oss_present;
301da177e4SLinus Torvalds volatile struct mac_oss *oss;
311da177e4SLinus Torvalds 
321da177e4SLinus Torvalds /*
331da177e4SLinus Torvalds  * Initialize the OSS
341da177e4SLinus Torvalds  *
351da177e4SLinus Torvalds  * The OSS "detection" code is actually in via_init() which is always called
361da177e4SLinus Torvalds  * before us. Thus we can count on oss_present being valid on entry.
371da177e4SLinus Torvalds  */
381da177e4SLinus Torvalds 
391da177e4SLinus Torvalds void __init oss_init(void)
401da177e4SLinus Torvalds {
411da177e4SLinus Torvalds 	int i;
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds 	if (!oss_present) return;
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds 	oss = (struct mac_oss *) OSS_BASE;
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds 	/* Disable all interrupts. Unlike a VIA it looks like we    */
481da177e4SLinus Torvalds 	/* do this by setting the source's interrupt level to zero. */
491da177e4SLinus Torvalds 
50b24f670bSFinn Thain 	for (i = 0; i < OSS_NUM_SOURCES; i++)
51da3fb3c9SFinn Thain 		oss->irq_level[i] = 0;
521da177e4SLinus Torvalds }
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds /*
551da177e4SLinus Torvalds  * Initialize OSS for Nubus access
561da177e4SLinus Torvalds  */
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds void __init oss_nubus_init(void)
591da177e4SLinus Torvalds {
601da177e4SLinus Torvalds }
611da177e4SLinus Torvalds 
621da177e4SLinus Torvalds /*
63da3fb3c9SFinn Thain  * Handle miscellaneous OSS interrupts.
641da177e4SLinus Torvalds  */
651da177e4SLinus Torvalds 
66*bd0b9ac4SThomas Gleixner static void oss_irq(struct irq_desc *desc)
679145db56SGeert Uytterhoeven {
68da3fb3c9SFinn Thain 	int events = oss->irq_pending &
69da3fb3c9SFinn Thain 		(OSS_IP_IOPSCC | OSS_IP_SCSI | OSS_IP_IOPISM);
709145db56SGeert Uytterhoeven 
719145db56SGeert Uytterhoeven #ifdef DEBUG_IRQS
729145db56SGeert Uytterhoeven 	if ((console_loglevel == 10) && !(events & OSS_IP_SCSI)) {
73625b86adSThomas Gleixner 		unsigned int irq = irq_desc_get_irq(desc);
74625b86adSThomas Gleixner 
759145db56SGeert Uytterhoeven 		printk("oss_irq: irq %u events = 0x%04X\n", irq,
769145db56SGeert Uytterhoeven 			(int) oss->irq_pending);
779145db56SGeert Uytterhoeven 	}
789145db56SGeert Uytterhoeven #endif
799145db56SGeert Uytterhoeven 
80da3fb3c9SFinn Thain 	if (events & OSS_IP_IOPSCC) {
81da3fb3c9SFinn Thain 		oss->irq_pending &= ~OSS_IP_IOPSCC;
82da3fb3c9SFinn Thain 		generic_handle_irq(IRQ_MAC_SCC);
83da3fb3c9SFinn Thain 	}
84da3fb3c9SFinn Thain 
85da3fb3c9SFinn Thain 	if (events & OSS_IP_SCSI) {
869145db56SGeert Uytterhoeven 		oss->irq_pending &= ~OSS_IP_SCSI;
879145db56SGeert Uytterhoeven 		generic_handle_irq(IRQ_MAC_SCSI);
88da3fb3c9SFinn Thain 	}
89da3fb3c9SFinn Thain 
90da3fb3c9SFinn Thain 	if (events & OSS_IP_IOPISM) {
91da3fb3c9SFinn Thain 		oss->irq_pending &= ~OSS_IP_IOPISM;
92da3fb3c9SFinn Thain 		generic_handle_irq(IRQ_MAC_ADB);
939145db56SGeert Uytterhoeven 	}
949145db56SGeert Uytterhoeven }
951da177e4SLinus Torvalds 
961da177e4SLinus Torvalds /*
971da177e4SLinus Torvalds  * Nubus IRQ handler, OSS style
981da177e4SLinus Torvalds  *
991da177e4SLinus Torvalds  * Unlike the VIA/RBV this is on its own autovector interrupt level.
1001da177e4SLinus Torvalds  */
1011da177e4SLinus Torvalds 
102*bd0b9ac4SThomas Gleixner static void oss_nubus_irq(struct irq_desc *desc)
1039145db56SGeert Uytterhoeven {
1049145db56SGeert Uytterhoeven 	int events, irq_bit, i;
1059145db56SGeert Uytterhoeven 
1069145db56SGeert Uytterhoeven 	events = oss->irq_pending & OSS_IP_NUBUS;
1079145db56SGeert Uytterhoeven 	if (!events)
1089145db56SGeert Uytterhoeven 		return;
1099145db56SGeert Uytterhoeven 
1109145db56SGeert Uytterhoeven #ifdef DEBUG_NUBUS_INT
1119145db56SGeert Uytterhoeven 	if (console_loglevel > 7) {
1129145db56SGeert Uytterhoeven 		printk("oss_nubus_irq: events = 0x%04X\n", events);
1139145db56SGeert Uytterhoeven 	}
1149145db56SGeert Uytterhoeven #endif
1159145db56SGeert Uytterhoeven 	/* There are only six slots on the OSS, not seven */
1169145db56SGeert Uytterhoeven 
1179145db56SGeert Uytterhoeven 	i = 6;
1189145db56SGeert Uytterhoeven 	irq_bit = 0x40;
1199145db56SGeert Uytterhoeven 	do {
1209145db56SGeert Uytterhoeven 		--i;
1219145db56SGeert Uytterhoeven 		irq_bit >>= 1;
1229145db56SGeert Uytterhoeven 		if (events & irq_bit) {
1239145db56SGeert Uytterhoeven 			oss->irq_pending &= ~irq_bit;
1249145db56SGeert Uytterhoeven 			generic_handle_irq(NUBUS_SOURCE_BASE + i);
1259145db56SGeert Uytterhoeven 		}
1269145db56SGeert Uytterhoeven 	} while(events & (irq_bit - 1));
1279145db56SGeert Uytterhoeven }
1289145db56SGeert Uytterhoeven 
1299145db56SGeert Uytterhoeven /*
1309145db56SGeert Uytterhoeven  * Register the OSS and NuBus interrupt dispatchers.
131da3fb3c9SFinn Thain  *
132da3fb3c9SFinn Thain  * This IRQ mapping is laid out with two things in mind: first, we try to keep
133da3fb3c9SFinn Thain  * things on their own levels to avoid having to do double-dispatches. Second,
134da3fb3c9SFinn Thain  * the levels match as closely as possible the alternate IRQ mapping mode (aka
135da3fb3c9SFinn Thain  * "A/UX mode") available on some VIA machines.
1369145db56SGeert Uytterhoeven  */
1379145db56SGeert Uytterhoeven 
138da3fb3c9SFinn Thain #define OSS_IRQLEV_IOPISM    IRQ_AUTO_1
139da3fb3c9SFinn Thain #define OSS_IRQLEV_SCSI      IRQ_AUTO_2
140da3fb3c9SFinn Thain #define OSS_IRQLEV_NUBUS     IRQ_AUTO_3
141da3fb3c9SFinn Thain #define OSS_IRQLEV_IOPSCC    IRQ_AUTO_4
142da3fb3c9SFinn Thain #define OSS_IRQLEV_VIA1      IRQ_AUTO_6
143da3fb3c9SFinn Thain 
1449145db56SGeert Uytterhoeven void __init oss_register_interrupts(void)
1459145db56SGeert Uytterhoeven {
146da3fb3c9SFinn Thain 	irq_set_chained_handler(OSS_IRQLEV_IOPISM, oss_irq);
1479145db56SGeert Uytterhoeven 	irq_set_chained_handler(OSS_IRQLEV_SCSI,   oss_irq);
1489145db56SGeert Uytterhoeven 	irq_set_chained_handler(OSS_IRQLEV_NUBUS,  oss_nubus_irq);
149da3fb3c9SFinn Thain 	irq_set_chained_handler(OSS_IRQLEV_IOPSCC, oss_irq);
1509145db56SGeert Uytterhoeven 	irq_set_chained_handler(OSS_IRQLEV_VIA1,   via1_irq);
151da3fb3c9SFinn Thain 
152da3fb3c9SFinn Thain 	/* OSS_VIA1 gets enabled here because it has no machspec interrupt. */
153da3fb3c9SFinn Thain 	oss->irq_level[OSS_VIA1] = IRQ_AUTO_6;
1549145db56SGeert Uytterhoeven }
1551da177e4SLinus Torvalds 
1561da177e4SLinus Torvalds /*
1571da177e4SLinus Torvalds  * Enable an OSS interrupt
1581da177e4SLinus Torvalds  *
1591da177e4SLinus Torvalds  * It looks messy but it's rather straightforward. The switch() statement
1601da177e4SLinus Torvalds  * just maps the machspec interrupt numbers to the right OSS interrupt
1611da177e4SLinus Torvalds  * source (if the OSS handles that interrupt) and then sets the interrupt
1621da177e4SLinus Torvalds  * level for that source to nonzero, thus enabling the interrupt.
1631da177e4SLinus Torvalds  */
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds void oss_irq_enable(int irq) {
1661da177e4SLinus Torvalds #ifdef DEBUG_IRQUSE
1671da177e4SLinus Torvalds 	printk("oss_irq_enable(%d)\n", irq);
1681da177e4SLinus Torvalds #endif
1691da177e4SLinus Torvalds 	switch(irq) {
17080614e5aSFinn Thain 		case IRQ_MAC_SCC:
1711da177e4SLinus Torvalds 			oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_IOPSCC;
172da3fb3c9SFinn Thain 			return;
1731da177e4SLinus Torvalds 		case IRQ_MAC_ADB:
1741da177e4SLinus Torvalds 			oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_IOPISM;
175da3fb3c9SFinn Thain 			return;
1761da177e4SLinus Torvalds 		case IRQ_MAC_SCSI:
1771da177e4SLinus Torvalds 			oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI;
178da3fb3c9SFinn Thain 			return;
1791da177e4SLinus Torvalds 		case IRQ_NUBUS_9:
1801da177e4SLinus Torvalds 		case IRQ_NUBUS_A:
1811da177e4SLinus Torvalds 		case IRQ_NUBUS_B:
1821da177e4SLinus Torvalds 		case IRQ_NUBUS_C:
1831da177e4SLinus Torvalds 		case IRQ_NUBUS_D:
1841da177e4SLinus Torvalds 		case IRQ_NUBUS_E:
1851da177e4SLinus Torvalds 			irq -= NUBUS_SOURCE_BASE;
1861da177e4SLinus Torvalds 			oss->irq_level[irq] = OSS_IRQLEV_NUBUS;
187da3fb3c9SFinn Thain 			return;
1881da177e4SLinus Torvalds 	}
189da3fb3c9SFinn Thain 
190da3fb3c9SFinn Thain 	if (IRQ_SRC(irq) == 1)
191da3fb3c9SFinn Thain 		via_irq_enable(irq);
1921da177e4SLinus Torvalds }
1931da177e4SLinus Torvalds 
1941da177e4SLinus Torvalds /*
1951da177e4SLinus Torvalds  * Disable an OSS interrupt
1961da177e4SLinus Torvalds  *
1971da177e4SLinus Torvalds  * Same as above except we set the source's interrupt level to zero,
1981da177e4SLinus Torvalds  * to disable the interrupt.
1991da177e4SLinus Torvalds  */
2001da177e4SLinus Torvalds 
2011da177e4SLinus Torvalds void oss_irq_disable(int irq) {
2021da177e4SLinus Torvalds #ifdef DEBUG_IRQUSE
2031da177e4SLinus Torvalds 	printk("oss_irq_disable(%d)\n", irq);
2041da177e4SLinus Torvalds #endif
2051da177e4SLinus Torvalds 	switch(irq) {
20680614e5aSFinn Thain 		case IRQ_MAC_SCC:
207da3fb3c9SFinn Thain 			oss->irq_level[OSS_IOPSCC] = 0;
208da3fb3c9SFinn Thain 			return;
2091da177e4SLinus Torvalds 		case IRQ_MAC_ADB:
210da3fb3c9SFinn Thain 			oss->irq_level[OSS_IOPISM] = 0;
211da3fb3c9SFinn Thain 			return;
2121da177e4SLinus Torvalds 		case IRQ_MAC_SCSI:
213da3fb3c9SFinn Thain 			oss->irq_level[OSS_SCSI] = 0;
214da3fb3c9SFinn Thain 			return;
2151da177e4SLinus Torvalds 		case IRQ_NUBUS_9:
2161da177e4SLinus Torvalds 		case IRQ_NUBUS_A:
2171da177e4SLinus Torvalds 		case IRQ_NUBUS_B:
2181da177e4SLinus Torvalds 		case IRQ_NUBUS_C:
2191da177e4SLinus Torvalds 		case IRQ_NUBUS_D:
2201da177e4SLinus Torvalds 		case IRQ_NUBUS_E:
2211da177e4SLinus Torvalds 			irq -= NUBUS_SOURCE_BASE;
222da3fb3c9SFinn Thain 			oss->irq_level[irq] = 0;
223da3fb3c9SFinn Thain 			return;
2241da177e4SLinus Torvalds 	}
225da3fb3c9SFinn Thain 
226da3fb3c9SFinn Thain 	if (IRQ_SRC(irq) == 1)
227da3fb3c9SFinn Thain 		via_irq_disable(irq);
2281da177e4SLinus Torvalds }
229