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