Linux Audio

Check our new training course

Loading...
v6.8
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 *	Operating System Services (OSS) chip handling
  4 *	Written by Joshua M. Thompson (funaho@jurai.org)
  5 *
  6 *
  7 *	This chip is used in the IIfx in place of VIA #2. It acts like a fancy
  8 *	VIA chip with prorammable interrupt levels.
  9 *
 10 * 990502 (jmt) - Major rewrite for new interrupt architecture as well as some
 11 *		  recent insights into OSS operational details.
 12 * 990610 (jmt) - Now taking full advantage of the OSS. Interrupts are mapped
 13 *		  to mostly match the A/UX interrupt scheme supported on the
 14 *		  VIA side. Also added support for enabling the ISM irq again
 15 *		  since we now have a functional IOP manager.
 16 */
 17
 18#include <linux/types.h>
 19#include <linux/kernel.h>
 20#include <linux/mm.h>
 21#include <linux/delay.h>
 22#include <linux/init.h>
 23#include <linux/irq.h>
 24
 25#include <asm/macintosh.h>
 26#include <asm/macints.h>
 27#include <asm/mac_via.h>
 28#include <asm/mac_oss.h>
 29
 30#include "mac.h"
 31
 32int oss_present;
 33volatile struct mac_oss *oss;
 34
 35/*
 36 * Initialize the OSS
 
 
 
 37 */
 38
 39void __init oss_init(void)
 40{
 41	int i;
 42
 43	if (macintosh_config->ident != MAC_MODEL_IIFX)
 44		return;
 45
 46	oss = (struct mac_oss *) OSS_BASE;
 47	pr_debug("OSS detected at %p", oss);
 48	oss_present = 1;
 49
 50	/* Disable all interrupts. Unlike a VIA it looks like we    */
 51	/* do this by setting the source's interrupt level to zero. */
 52
 53	for (i = 0; i < OSS_NUM_SOURCES; i++)
 54		oss->irq_level[i] = 0;
 
 55}
 56
 57/*
 58 * Handle OSS interrupts.
 59 * XXX how do you clear a pending IRQ? is it even necessary?
 60 */
 61
 62static void oss_iopism_irq(struct irq_desc *desc)
 63{
 64	generic_handle_irq(IRQ_MAC_ADB);
 65}
 66
 67static void oss_scsi_irq(struct irq_desc *desc)
 
 
 
 
 68{
 69	generic_handle_irq(IRQ_MAC_SCSI);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 70}
 71
 72static void oss_nubus_irq(struct irq_desc *desc)
 
 
 
 
 
 
 73{
 74	u16 events, irq_bit;
 75	int irq_num;
 76
 77	events = oss->irq_pending & OSS_IP_NUBUS;
 78	irq_num = NUBUS_SOURCE_BASE + 5;
 79	irq_bit = OSS_IP_NUBUS5;
 
 
 
 
 
 
 
 
 
 
 80	do {
 
 
 81		if (events & irq_bit) {
 82			events &= ~irq_bit;
 83			generic_handle_irq(irq_num);
 84		}
 85		--irq_num;
 86		irq_bit >>= 1;
 87	} while (events);
 88}
 89
 90static void oss_iopscc_irq(struct irq_desc *desc)
 91{
 92	generic_handle_irq(IRQ_MAC_SCC);
 93}
 94
 95/*
 96 * Register the OSS and NuBus interrupt dispatchers.
 97 *
 98 * This IRQ mapping is laid out with two things in mind: first, we try to keep
 99 * things on their own levels to avoid having to do double-dispatches. Second,
100 * the levels match as closely as possible the alternate IRQ mapping mode (aka
101 * "A/UX mode") available on some VIA machines.
102 */
103
104#define OSS_IRQLEV_IOPISM    IRQ_AUTO_1
105#define OSS_IRQLEV_SCSI      IRQ_AUTO_2
106#define OSS_IRQLEV_NUBUS     IRQ_AUTO_3
107#define OSS_IRQLEV_IOPSCC    IRQ_AUTO_4
108#define OSS_IRQLEV_VIA1      IRQ_AUTO_6
109
110void __init oss_register_interrupts(void)
111{
112	irq_set_chained_handler(OSS_IRQLEV_IOPISM, oss_iopism_irq);
113	irq_set_chained_handler(OSS_IRQLEV_SCSI,   oss_scsi_irq);
114	irq_set_chained_handler(OSS_IRQLEV_NUBUS,  oss_nubus_irq);
115	irq_set_chained_handler(OSS_IRQLEV_IOPSCC, oss_iopscc_irq);
116	irq_set_chained_handler(OSS_IRQLEV_VIA1,   via1_irq);
117
118	/* OSS_VIA1 gets enabled here because it has no machspec interrupt. */
119	oss->irq_level[OSS_VIA1] = OSS_IRQLEV_VIA1;
120}
121
122/*
123 * Enable an OSS interrupt
124 *
125 * It looks messy but it's rather straightforward. The switch() statement
126 * just maps the machspec interrupt numbers to the right OSS interrupt
127 * source (if the OSS handles that interrupt) and then sets the interrupt
128 * level for that source to nonzero, thus enabling the interrupt.
129 */
130
131void oss_irq_enable(int irq) {
 
 
 
132	switch(irq) {
133		case IRQ_MAC_SCC:
134			oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_IOPSCC;
135			return;
136		case IRQ_MAC_ADB:
137			oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_IOPISM;
138			return;
139		case IRQ_MAC_SCSI:
140			oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI;
141			return;
142		case IRQ_NUBUS_9:
143		case IRQ_NUBUS_A:
144		case IRQ_NUBUS_B:
145		case IRQ_NUBUS_C:
146		case IRQ_NUBUS_D:
147		case IRQ_NUBUS_E:
148			irq -= NUBUS_SOURCE_BASE;
149			oss->irq_level[irq] = OSS_IRQLEV_NUBUS;
150			return;
151	}
152
153	if (IRQ_SRC(irq) == 1)
154		via_irq_enable(irq);
155}
156
157/*
158 * Disable an OSS interrupt
159 *
160 * Same as above except we set the source's interrupt level to zero,
161 * to disable the interrupt.
162 */
163
164void oss_irq_disable(int irq) {
 
 
 
165	switch(irq) {
166		case IRQ_MAC_SCC:
167			oss->irq_level[OSS_IOPSCC] = 0;
168			return;
169		case IRQ_MAC_ADB:
170			oss->irq_level[OSS_IOPISM] = 0;
171			return;
172		case IRQ_MAC_SCSI:
173			oss->irq_level[OSS_SCSI] = 0;
174			return;
175		case IRQ_NUBUS_9:
176		case IRQ_NUBUS_A:
177		case IRQ_NUBUS_B:
178		case IRQ_NUBUS_C:
179		case IRQ_NUBUS_D:
180		case IRQ_NUBUS_E:
181			irq -= NUBUS_SOURCE_BASE;
182			oss->irq_level[irq] = 0;
183			return;
184	}
185
186	if (IRQ_SRC(irq) == 1)
187		via_irq_disable(irq);
188}
v3.15
 
  1/*
  2 *	Operating System Services (OSS) chip handling
  3 *	Written by Joshua M. Thompson (funaho@jurai.org)
  4 *
  5 *
  6 *	This chip is used in the IIfx in place of VIA #2. It acts like a fancy
  7 *	VIA chip with prorammable interrupt levels.
  8 *
  9 * 990502 (jmt) - Major rewrite for new interrupt architecture as well as some
 10 *		  recent insights into OSS operational details.
 11 * 990610 (jmt) - Now taking full advantage of the OSS. Interrupts are mapped
 12 *		  to mostly match the A/UX interrupt scheme supported on the
 13 *		  VIA side. Also added support for enabling the ISM irq again
 14 *		  since we now have a functional IOP manager.
 15 */
 16
 17#include <linux/types.h>
 18#include <linux/kernel.h>
 19#include <linux/mm.h>
 20#include <linux/delay.h>
 21#include <linux/init.h>
 22#include <linux/irq.h>
 23
 24#include <asm/macintosh.h>
 25#include <asm/macints.h>
 26#include <asm/mac_via.h>
 27#include <asm/mac_oss.h>
 28
 
 
 29int oss_present;
 30volatile struct mac_oss *oss;
 31
 32/*
 33 * Initialize the OSS
 34 *
 35 * The OSS "detection" code is actually in via_init() which is always called
 36 * before us. Thus we can count on oss_present being valid on entry.
 37 */
 38
 39void __init oss_init(void)
 40{
 41	int i;
 42
 43	if (!oss_present) return;
 
 44
 45	oss = (struct mac_oss *) OSS_BASE;
 
 
 46
 47	/* Disable all interrupts. Unlike a VIA it looks like we    */
 48	/* do this by setting the source's interrupt level to zero. */
 49
 50	for (i = 0; i <= OSS_NUM_SOURCES; i++) {
 51		oss->irq_level[i] = 0;
 52	}
 53}
 54
 55/*
 56 * Initialize OSS for Nubus access
 
 57 */
 58
 59void __init oss_nubus_init(void)
 60{
 
 61}
 62
 63/*
 64 * Handle miscellaneous OSS interrupts.
 65 */
 66
 67static void oss_irq(unsigned int irq, struct irq_desc *desc)
 68{
 69	int events = oss->irq_pending &
 70	             (OSS_IP_IOPSCC | OSS_IP_SCSI | OSS_IP_IOPISM);
 71
 72#ifdef DEBUG_IRQS
 73	if ((console_loglevel == 10) && !(events & OSS_IP_SCSI)) {
 74		printk("oss_irq: irq %u events = 0x%04X\n", irq,
 75			(int) oss->irq_pending);
 76	}
 77#endif
 78
 79	if (events & OSS_IP_IOPSCC) {
 80		oss->irq_pending &= ~OSS_IP_IOPSCC;
 81		generic_handle_irq(IRQ_MAC_SCC);
 82	}
 83
 84	if (events & OSS_IP_SCSI) {
 85		oss->irq_pending &= ~OSS_IP_SCSI;
 86		generic_handle_irq(IRQ_MAC_SCSI);
 87	}
 88
 89	if (events & OSS_IP_IOPISM) {
 90		oss->irq_pending &= ~OSS_IP_IOPISM;
 91		generic_handle_irq(IRQ_MAC_ADB);
 92	}
 93}
 94
 95/*
 96 * Nubus IRQ handler, OSS style
 97 *
 98 * Unlike the VIA/RBV this is on its own autovector interrupt level.
 99 */
100
101static void oss_nubus_irq(unsigned int irq, struct irq_desc *desc)
102{
103	int events, irq_bit, i;
 
104
105	events = oss->irq_pending & OSS_IP_NUBUS;
106	if (!events)
107		return;
108
109#ifdef DEBUG_NUBUS_INT
110	if (console_loglevel > 7) {
111		printk("oss_nubus_irq: events = 0x%04X\n", events);
112	}
113#endif
114	/* There are only six slots on the OSS, not seven */
115
116	i = 6;
117	irq_bit = 0x40;
118	do {
119		--i;
120		irq_bit >>= 1;
121		if (events & irq_bit) {
122			oss->irq_pending &= ~irq_bit;
123			generic_handle_irq(NUBUS_SOURCE_BASE + i);
124		}
125	} while(events & (irq_bit - 1));
 
 
 
 
 
 
 
126}
127
128/*
129 * Register the OSS and NuBus interrupt dispatchers.
130 *
131 * This IRQ mapping is laid out with two things in mind: first, we try to keep
132 * things on their own levels to avoid having to do double-dispatches. Second,
133 * the levels match as closely as possible the alternate IRQ mapping mode (aka
134 * "A/UX mode") available on some VIA machines.
135 */
136
137#define OSS_IRQLEV_IOPISM    IRQ_AUTO_1
138#define OSS_IRQLEV_SCSI      IRQ_AUTO_2
139#define OSS_IRQLEV_NUBUS     IRQ_AUTO_3
140#define OSS_IRQLEV_IOPSCC    IRQ_AUTO_4
141#define OSS_IRQLEV_VIA1      IRQ_AUTO_6
142
143void __init oss_register_interrupts(void)
144{
145	irq_set_chained_handler(OSS_IRQLEV_IOPISM, oss_irq);
146	irq_set_chained_handler(OSS_IRQLEV_SCSI,   oss_irq);
147	irq_set_chained_handler(OSS_IRQLEV_NUBUS,  oss_nubus_irq);
148	irq_set_chained_handler(OSS_IRQLEV_IOPSCC, oss_irq);
149	irq_set_chained_handler(OSS_IRQLEV_VIA1,   via1_irq);
150
151	/* OSS_VIA1 gets enabled here because it has no machspec interrupt. */
152	oss->irq_level[OSS_VIA1] = IRQ_AUTO_6;
153}
154
155/*
156 * Enable an OSS interrupt
157 *
158 * It looks messy but it's rather straightforward. The switch() statement
159 * just maps the machspec interrupt numbers to the right OSS interrupt
160 * source (if the OSS handles that interrupt) and then sets the interrupt
161 * level for that source to nonzero, thus enabling the interrupt.
162 */
163
164void oss_irq_enable(int irq) {
165#ifdef DEBUG_IRQUSE
166	printk("oss_irq_enable(%d)\n", irq);
167#endif
168	switch(irq) {
169		case IRQ_MAC_SCC:
170			oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_IOPSCC;
171			return;
172		case IRQ_MAC_ADB:
173			oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_IOPISM;
174			return;
175		case IRQ_MAC_SCSI:
176			oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI;
177			return;
178		case IRQ_NUBUS_9:
179		case IRQ_NUBUS_A:
180		case IRQ_NUBUS_B:
181		case IRQ_NUBUS_C:
182		case IRQ_NUBUS_D:
183		case IRQ_NUBUS_E:
184			irq -= NUBUS_SOURCE_BASE;
185			oss->irq_level[irq] = OSS_IRQLEV_NUBUS;
186			return;
187	}
188
189	if (IRQ_SRC(irq) == 1)
190		via_irq_enable(irq);
191}
192
193/*
194 * Disable an OSS interrupt
195 *
196 * Same as above except we set the source's interrupt level to zero,
197 * to disable the interrupt.
198 */
199
200void oss_irq_disable(int irq) {
201#ifdef DEBUG_IRQUSE
202	printk("oss_irq_disable(%d)\n", irq);
203#endif
204	switch(irq) {
205		case IRQ_MAC_SCC:
206			oss->irq_level[OSS_IOPSCC] = 0;
207			return;
208		case IRQ_MAC_ADB:
209			oss->irq_level[OSS_IOPISM] = 0;
210			return;
211		case IRQ_MAC_SCSI:
212			oss->irq_level[OSS_SCSI] = 0;
213			return;
214		case IRQ_NUBUS_9:
215		case IRQ_NUBUS_A:
216		case IRQ_NUBUS_B:
217		case IRQ_NUBUS_C:
218		case IRQ_NUBUS_D:
219		case IRQ_NUBUS_E:
220			irq -= NUBUS_SOURCE_BASE;
221			oss->irq_level[irq] = 0;
222			return;
223	}
224
225	if (IRQ_SRC(irq) == 1)
226		via_irq_disable(irq);
227}