oss.c 5.31 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
/*
Finn Thain's avatar
Finn Thain committed
2
 *	Operating System Services (OSS) chip handling
Linus Torvalds's avatar
Linus Torvalds committed
3 4 5 6 7 8 9 10
 *	Written by Joshua M. Thompson (funaho@jurai.org)
 *
 *
 *	This chip is used in the IIfx in place of VIA #2. It acts like a fancy
 *	VIA chip with prorammable interrupt levels.
 *
 * 990502 (jmt) - Major rewrite for new interrupt architecture as well as some
 *		  recent insights into OSS operational details.
Simon Arlott's avatar
Simon Arlott committed
11
 * 990610 (jmt) - Now taking full advantage of the OSS. Interrupts are mapped
Linus Torvalds's avatar
Linus Torvalds committed
12 13 14 15 16 17 18 19 20 21
 *		  to mostly match the A/UX interrupt scheme supported on the
 *		  VIA side. Also added support for enabling the ISM irq again
 *		  since we now have a functional IOP manager.
 */

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/delay.h>
#include <linux/init.h>
22
#include <linux/irq.h>
Linus Torvalds's avatar
Linus Torvalds committed
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

#include <asm/macintosh.h>
#include <asm/macints.h>
#include <asm/mac_via.h>
#include <asm/mac_oss.h>

int oss_present;
volatile struct mac_oss *oss;

/*
 * Initialize the OSS
 *
 * The OSS "detection" code is actually in via_init() which is always called
 * before us. Thus we can count on oss_present being valid on entry.
 */

void __init oss_init(void)
{
	int i;

	if (!oss_present) return;

	oss = (struct mac_oss *) OSS_BASE;

	/* Disable all interrupts. Unlike a VIA it looks like we    */
	/* do this by setting the source's interrupt level to zero. */

50
	for (i = 0; i < OSS_NUM_SOURCES; i++)
Finn Thain's avatar
Finn Thain committed
51
		oss->irq_level[i] = 0;
Linus Torvalds's avatar
Linus Torvalds committed
52 53 54 55 56 57 58 59 60 61 62
}

/*
 * Initialize OSS for Nubus access
 */

void __init oss_nubus_init(void)
{
}

/*
Finn Thain's avatar
Finn Thain committed
63
 * Handle miscellaneous OSS interrupts.
Linus Torvalds's avatar
Linus Torvalds committed
64 65
 */

66
static void oss_irq(struct irq_desc *desc)
67
{
Finn Thain's avatar
Finn Thain committed
68
	int events = oss->irq_pending &
69
		(OSS_IP_IOPSCC | OSS_IP_SCSI | OSS_IP_IOPISM);
70 71 72

#ifdef DEBUG_IRQS
	if ((console_loglevel == 10) && !(events & OSS_IP_SCSI)) {
73 74
		unsigned int irq = irq_desc_get_irq(desc);

75 76 77 78 79
		printk("oss_irq: irq %u events = 0x%04X\n", irq,
			(int) oss->irq_pending);
	}
#endif

Finn Thain's avatar
Finn Thain committed
80 81 82 83 84 85
	if (events & OSS_IP_IOPSCC) {
		oss->irq_pending &= ~OSS_IP_IOPSCC;
		generic_handle_irq(IRQ_MAC_SCC);
	}

	if (events & OSS_IP_SCSI) {
86 87
		oss->irq_pending &= ~OSS_IP_SCSI;
		generic_handle_irq(IRQ_MAC_SCSI);
Finn Thain's avatar
Finn Thain committed
88 89 90 91 92
	}

	if (events & OSS_IP_IOPISM) {
		oss->irq_pending &= ~OSS_IP_IOPISM;
		generic_handle_irq(IRQ_MAC_ADB);
93 94
	}
}
Linus Torvalds's avatar
Linus Torvalds committed
95 96 97 98 99 100 101

/*
 * Nubus IRQ handler, OSS style
 *
 * Unlike the VIA/RBV this is on its own autovector interrupt level.
 */

102
static void oss_nubus_irq(struct irq_desc *desc)
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
{
	int events, irq_bit, i;

	events = oss->irq_pending & OSS_IP_NUBUS;
	if (!events)
		return;

#ifdef DEBUG_NUBUS_INT
	if (console_loglevel > 7) {
		printk("oss_nubus_irq: events = 0x%04X\n", events);
	}
#endif
	/* There are only six slots on the OSS, not seven */

	i = 6;
	irq_bit = 0x40;
	do {
		--i;
		irq_bit >>= 1;
		if (events & irq_bit) {
			oss->irq_pending &= ~irq_bit;
			generic_handle_irq(NUBUS_SOURCE_BASE + i);
		}
	} while(events & (irq_bit - 1));
}

/*
 * Register the OSS and NuBus interrupt dispatchers.
Finn Thain's avatar
Finn Thain committed
131 132 133 134 135
 *
 * This IRQ mapping is laid out with two things in mind: first, we try to keep
 * things on their own levels to avoid having to do double-dispatches. Second,
 * the levels match as closely as possible the alternate IRQ mapping mode (aka
 * "A/UX mode") available on some VIA machines.
136 137
 */

Finn Thain's avatar
Finn Thain committed
138 139 140 141 142 143
#define OSS_IRQLEV_IOPISM    IRQ_AUTO_1
#define OSS_IRQLEV_SCSI      IRQ_AUTO_2
#define OSS_IRQLEV_NUBUS     IRQ_AUTO_3
#define OSS_IRQLEV_IOPSCC    IRQ_AUTO_4
#define OSS_IRQLEV_VIA1      IRQ_AUTO_6

144 145
void __init oss_register_interrupts(void)
{
Finn Thain's avatar
Finn Thain committed
146 147 148 149 150 151 152 153
	irq_set_chained_handler(OSS_IRQLEV_IOPISM, oss_irq);
	irq_set_chained_handler(OSS_IRQLEV_SCSI,   oss_irq);
	irq_set_chained_handler(OSS_IRQLEV_NUBUS,  oss_nubus_irq);
	irq_set_chained_handler(OSS_IRQLEV_IOPSCC, oss_irq);
	irq_set_chained_handler(OSS_IRQLEV_VIA1,   via1_irq);

	/* OSS_VIA1 gets enabled here because it has no machspec interrupt. */
	oss->irq_level[OSS_VIA1] = IRQ_AUTO_6;
154
}
Linus Torvalds's avatar
Linus Torvalds committed
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169

/*
 * Enable an OSS interrupt
 *
 * It looks messy but it's rather straightforward. The switch() statement
 * just maps the machspec interrupt numbers to the right OSS interrupt
 * source (if the OSS handles that interrupt) and then sets the interrupt
 * level for that source to nonzero, thus enabling the interrupt.
 */

void oss_irq_enable(int irq) {
#ifdef DEBUG_IRQUSE
	printk("oss_irq_enable(%d)\n", irq);
#endif
	switch(irq) {
170
		case IRQ_MAC_SCC:
Linus Torvalds's avatar
Linus Torvalds committed
171
			oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_IOPSCC;
Finn Thain's avatar
Finn Thain committed
172
			return;
Linus Torvalds's avatar
Linus Torvalds committed
173 174
		case IRQ_MAC_ADB:
			oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_IOPISM;
Finn Thain's avatar
Finn Thain committed
175
			return;
Linus Torvalds's avatar
Linus Torvalds committed
176 177
		case IRQ_MAC_SCSI:
			oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI;
Finn Thain's avatar
Finn Thain committed
178
			return;
Linus Torvalds's avatar
Linus Torvalds committed
179 180 181 182 183 184 185 186
		case IRQ_NUBUS_9:
		case IRQ_NUBUS_A:
		case IRQ_NUBUS_B:
		case IRQ_NUBUS_C:
		case IRQ_NUBUS_D:
		case IRQ_NUBUS_E:
			irq -= NUBUS_SOURCE_BASE;
			oss->irq_level[irq] = OSS_IRQLEV_NUBUS;
Finn Thain's avatar
Finn Thain committed
187
			return;
Linus Torvalds's avatar
Linus Torvalds committed
188
	}
Finn Thain's avatar
Finn Thain committed
189 190 191

	if (IRQ_SRC(irq) == 1)
		via_irq_enable(irq);
Linus Torvalds's avatar
Linus Torvalds committed
192 193 194 195 196 197 198 199 200 201 202 203 204 205
}

/*
 * Disable an OSS interrupt
 *
 * Same as above except we set the source's interrupt level to zero,
 * to disable the interrupt.
 */

void oss_irq_disable(int irq) {
#ifdef DEBUG_IRQUSE
	printk("oss_irq_disable(%d)\n", irq);
#endif
	switch(irq) {
206
		case IRQ_MAC_SCC:
Finn Thain's avatar
Finn Thain committed
207 208
			oss->irq_level[OSS_IOPSCC] = 0;
			return;
Linus Torvalds's avatar
Linus Torvalds committed
209
		case IRQ_MAC_ADB:
Finn Thain's avatar
Finn Thain committed
210 211
			oss->irq_level[OSS_IOPISM] = 0;
			return;
Linus Torvalds's avatar
Linus Torvalds committed
212
		case IRQ_MAC_SCSI:
Finn Thain's avatar
Finn Thain committed
213 214
			oss->irq_level[OSS_SCSI] = 0;
			return;
Linus Torvalds's avatar
Linus Torvalds committed
215 216 217 218 219 220 221
		case IRQ_NUBUS_9:
		case IRQ_NUBUS_A:
		case IRQ_NUBUS_B:
		case IRQ_NUBUS_C:
		case IRQ_NUBUS_D:
		case IRQ_NUBUS_E:
			irq -= NUBUS_SOURCE_BASE;
Finn Thain's avatar
Finn Thain committed
222 223
			oss->irq_level[irq] = 0;
			return;
Linus Torvalds's avatar
Linus Torvalds committed
224
	}
Finn Thain's avatar
Finn Thain committed
225 226 227

	if (IRQ_SRC(irq) == 1)
		via_irq_disable(irq);
Linus Torvalds's avatar
Linus Torvalds committed
228
}