1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * 6522 Versatile Interface Adapter (VIA)
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * There are two of these on the Mac II. Some IRQs are vectored
6*4882a593Smuzhiyun * via them as are assorted bits and bobs - eg RTC, ADB.
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * CSA: Motorola seems to have removed documentation on the 6522 from
9*4882a593Smuzhiyun * their web site; try
10*4882a593Smuzhiyun * http://nerini.drf.com/vectrex/other/text/chips/6522/
11*4882a593Smuzhiyun * http://www.zymurgy.net/classic/vic20/vicdet1.htm
12*4882a593Smuzhiyun * and
13*4882a593Smuzhiyun * http://193.23.168.87/mikro_laborversuche/via_iobaustein/via6522_1.html
14*4882a593Smuzhiyun * for info. A full-text web search on 6522 AND VIA will probably also
15*4882a593Smuzhiyun * net some usefulness. <cananian@alumni.princeton.edu> 20apr1999
16*4882a593Smuzhiyun *
17*4882a593Smuzhiyun * Additional data is here (the SY6522 was used in the Mac II etc):
18*4882a593Smuzhiyun * http://www.6502.org/documents/datasheets/synertek/synertek_sy6522.pdf
19*4882a593Smuzhiyun * http://www.6502.org/documents/datasheets/synertek/synertek_sy6522_programming_reference.pdf
20*4882a593Smuzhiyun *
21*4882a593Smuzhiyun * PRAM/RTC access algorithms are from the NetBSD RTC toolkit version 1.08b
22*4882a593Smuzhiyun * by Erik Vogan and adapted to Linux by Joshua M. Thompson (funaho@jurai.org)
23*4882a593Smuzhiyun *
24*4882a593Smuzhiyun */
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun #include <linux/clocksource.h>
27*4882a593Smuzhiyun #include <linux/types.h>
28*4882a593Smuzhiyun #include <linux/kernel.h>
29*4882a593Smuzhiyun #include <linux/mm.h>
30*4882a593Smuzhiyun #include <linux/delay.h>
31*4882a593Smuzhiyun #include <linux/init.h>
32*4882a593Smuzhiyun #include <linux/module.h>
33*4882a593Smuzhiyun #include <linux/irq.h>
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun #include <asm/macintosh.h>
36*4882a593Smuzhiyun #include <asm/macints.h>
37*4882a593Smuzhiyun #include <asm/mac_via.h>
38*4882a593Smuzhiyun #include <asm/mac_psc.h>
39*4882a593Smuzhiyun #include <asm/mac_oss.h>
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun volatile __u8 *via1, *via2;
42*4882a593Smuzhiyun int rbv_present;
43*4882a593Smuzhiyun int via_alt_mapping;
44*4882a593Smuzhiyun EXPORT_SYMBOL(via_alt_mapping);
45*4882a593Smuzhiyun static __u8 rbv_clear;
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun /*
48*4882a593Smuzhiyun * Globals for accessing the VIA chip registers without having to
49*4882a593Smuzhiyun * check if we're hitting a real VIA or an RBV. Normally you could
50*4882a593Smuzhiyun * just hit the combined register (ie, vIER|rIER) but that seems to
51*4882a593Smuzhiyun * break on AV Macs...probably because they actually decode more than
52*4882a593Smuzhiyun * eight address bits. Why can't Apple engineers at least be
53*4882a593Smuzhiyun * _consistently_ lazy? - 1999-05-21 (jmt)
54*4882a593Smuzhiyun */
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun static int gIER,gIFR,gBufA,gBufB;
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun /*
59*4882a593Smuzhiyun * On Macs with a genuine VIA chip there is no way to mask an individual slot
60*4882a593Smuzhiyun * interrupt. This limitation also seems to apply to VIA clone logic cores in
61*4882a593Smuzhiyun * Quadra-like ASICs. (RBV and OSS machines don't have this limitation.)
62*4882a593Smuzhiyun *
63*4882a593Smuzhiyun * We used to fake it by configuring the relevant VIA pin as an output
64*4882a593Smuzhiyun * (to mask the interrupt) or input (to unmask). That scheme did not work on
65*4882a593Smuzhiyun * (at least) the Quadra 700. A NuBus card's /NMRQ signal is an open-collector
66*4882a593Smuzhiyun * circuit (see Designing Cards and Drivers for Macintosh II and Macintosh SE,
67*4882a593Smuzhiyun * p. 10-11 etc) but VIA outputs are not (see datasheet).
68*4882a593Smuzhiyun *
69*4882a593Smuzhiyun * Driving these outputs high must cause the VIA to source current and the
70*4882a593Smuzhiyun * card to sink current when it asserts /NMRQ. Current will flow but the pin
71*4882a593Smuzhiyun * voltage is uncertain and so the /NMRQ condition may still cause a transition
72*4882a593Smuzhiyun * at the VIA2 CA1 input (which explains the lost interrupts). A side effect
73*4882a593Smuzhiyun * is that a disabled slot IRQ can never be tested as pending or not.
74*4882a593Smuzhiyun *
75*4882a593Smuzhiyun * Driving these outputs low doesn't work either. All the slot /NMRQ lines are
76*4882a593Smuzhiyun * (active low) OR'd together to generate the CA1 (aka "SLOTS") interrupt (see
77*4882a593Smuzhiyun * The Guide To Macintosh Family Hardware, 2nd edition p. 167). If we drive a
78*4882a593Smuzhiyun * disabled /NMRQ line low, the falling edge immediately triggers a CA1
79*4882a593Smuzhiyun * interrupt and all slot interrupts after that will generate no transition
80*4882a593Smuzhiyun * and therefore no interrupt, even after being re-enabled.
81*4882a593Smuzhiyun *
82*4882a593Smuzhiyun * So we make the VIA port A I/O lines inputs and use nubus_disabled to keep
83*4882a593Smuzhiyun * track of their states. When any slot IRQ becomes disabled we mask the CA1
84*4882a593Smuzhiyun * umbrella interrupt. Only when all slot IRQs become enabled do we unmask
85*4882a593Smuzhiyun * the CA1 interrupt. It must remain enabled even when cards have no interrupt
86*4882a593Smuzhiyun * handler registered. Drivers must therefore disable a slot interrupt at the
87*4882a593Smuzhiyun * device before they call free_irq (like shared and autovector interrupts).
88*4882a593Smuzhiyun *
89*4882a593Smuzhiyun * There is also a related problem when MacOS is used to boot Linux. A network
90*4882a593Smuzhiyun * card brought up by a MacOS driver may raise an interrupt while Linux boots.
91*4882a593Smuzhiyun * This can be fatal since it can't be handled until the right driver loads
92*4882a593Smuzhiyun * (if such a driver exists at all). Apparently related to this hardware
93*4882a593Smuzhiyun * limitation, "Designing Cards and Drivers", p. 9-8, says that a slot
94*4882a593Smuzhiyun * interrupt with no driver would crash MacOS (the book was written before
95*4882a593Smuzhiyun * the appearance of Macs with RBV or OSS).
96*4882a593Smuzhiyun */
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun static u8 nubus_disabled;
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun void via_debug_dump(void);
101*4882a593Smuzhiyun static void via_nubus_init(void);
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun /*
104*4882a593Smuzhiyun * Initialize the VIAs
105*4882a593Smuzhiyun *
106*4882a593Smuzhiyun * First we figure out where they actually _are_ as well as what type of
107*4882a593Smuzhiyun * VIA we have for VIA2 (it could be a real VIA or an RBV or even an OSS.)
108*4882a593Smuzhiyun * Then we pretty much clear them out and disable all IRQ sources.
109*4882a593Smuzhiyun */
110*4882a593Smuzhiyun
via_init(void)111*4882a593Smuzhiyun void __init via_init(void)
112*4882a593Smuzhiyun {
113*4882a593Smuzhiyun via1 = (void *)VIA1_BASE;
114*4882a593Smuzhiyun pr_debug("VIA1 detected at %p\n", via1);
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun if (oss_present) {
117*4882a593Smuzhiyun via2 = NULL;
118*4882a593Smuzhiyun rbv_present = 0;
119*4882a593Smuzhiyun } else {
120*4882a593Smuzhiyun switch (macintosh_config->via_type) {
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun /* IIci, IIsi, IIvx, IIvi (P6xx), LC series */
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun case MAC_VIA_IICI:
125*4882a593Smuzhiyun via2 = (void *)RBV_BASE;
126*4882a593Smuzhiyun pr_debug("VIA2 (RBV) detected at %p\n", via2);
127*4882a593Smuzhiyun rbv_present = 1;
128*4882a593Smuzhiyun if (macintosh_config->ident == MAC_MODEL_LCIII) {
129*4882a593Smuzhiyun rbv_clear = 0x00;
130*4882a593Smuzhiyun } else {
131*4882a593Smuzhiyun /* on most RBVs (& unlike the VIAs), you */
132*4882a593Smuzhiyun /* need to set bit 7 when you write to IFR */
133*4882a593Smuzhiyun /* in order for your clear to occur. */
134*4882a593Smuzhiyun rbv_clear = 0x80;
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun gIER = rIER;
137*4882a593Smuzhiyun gIFR = rIFR;
138*4882a593Smuzhiyun gBufA = rSIFR;
139*4882a593Smuzhiyun gBufB = rBufB;
140*4882a593Smuzhiyun break;
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun /* Quadra and early MacIIs agree on the VIA locations */
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun case MAC_VIA_QUADRA:
145*4882a593Smuzhiyun case MAC_VIA_II:
146*4882a593Smuzhiyun via2 = (void *) VIA2_BASE;
147*4882a593Smuzhiyun pr_debug("VIA2 detected at %p\n", via2);
148*4882a593Smuzhiyun rbv_present = 0;
149*4882a593Smuzhiyun rbv_clear = 0x00;
150*4882a593Smuzhiyun gIER = vIER;
151*4882a593Smuzhiyun gIFR = vIFR;
152*4882a593Smuzhiyun gBufA = vBufA;
153*4882a593Smuzhiyun gBufB = vBufB;
154*4882a593Smuzhiyun break;
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun default:
157*4882a593Smuzhiyun panic("UNKNOWN VIA TYPE");
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun }
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun #ifdef DEBUG_VIA
162*4882a593Smuzhiyun via_debug_dump();
163*4882a593Smuzhiyun #endif
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun /*
166*4882a593Smuzhiyun * Shut down all IRQ sources, reset the timers, and
167*4882a593Smuzhiyun * kill the timer latch on VIA1.
168*4882a593Smuzhiyun */
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun via1[vIER] = 0x7F;
171*4882a593Smuzhiyun via1[vIFR] = 0x7F;
172*4882a593Smuzhiyun via1[vT1LL] = 0;
173*4882a593Smuzhiyun via1[vT1LH] = 0;
174*4882a593Smuzhiyun via1[vT1CL] = 0;
175*4882a593Smuzhiyun via1[vT1CH] = 0;
176*4882a593Smuzhiyun via1[vT2CL] = 0;
177*4882a593Smuzhiyun via1[vT2CH] = 0;
178*4882a593Smuzhiyun via1[vACR] &= ~0xC0; /* setup T1 timer with no PB7 output */
179*4882a593Smuzhiyun via1[vACR] &= ~0x03; /* disable port A & B latches */
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun /*
182*4882a593Smuzhiyun * SE/30: disable video IRQ
183*4882a593Smuzhiyun */
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun if (macintosh_config->ident == MAC_MODEL_SE30) {
186*4882a593Smuzhiyun via1[vDirB] |= 0x40;
187*4882a593Smuzhiyun via1[vBufB] |= 0x40;
188*4882a593Smuzhiyun }
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun switch (macintosh_config->adb_type) {
191*4882a593Smuzhiyun case MAC_ADB_IOP:
192*4882a593Smuzhiyun case MAC_ADB_II:
193*4882a593Smuzhiyun case MAC_ADB_PB1:
194*4882a593Smuzhiyun /*
195*4882a593Smuzhiyun * Set the RTC bits to a known state: all lines to outputs and
196*4882a593Smuzhiyun * RTC disabled (yes that's 0 to enable and 1 to disable).
197*4882a593Smuzhiyun */
198*4882a593Smuzhiyun via1[vDirB] |= VIA1B_vRTCEnb | VIA1B_vRTCClk | VIA1B_vRTCData;
199*4882a593Smuzhiyun via1[vBufB] |= VIA1B_vRTCEnb | VIA1B_vRTCClk;
200*4882a593Smuzhiyun break;
201*4882a593Smuzhiyun }
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun /* Everything below this point is VIA2/RBV only... */
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun if (oss_present)
206*4882a593Smuzhiyun return;
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun if ((macintosh_config->via_type == MAC_VIA_QUADRA) &&
209*4882a593Smuzhiyun (macintosh_config->adb_type != MAC_ADB_PB1) &&
210*4882a593Smuzhiyun (macintosh_config->adb_type != MAC_ADB_PB2) &&
211*4882a593Smuzhiyun (macintosh_config->ident != MAC_MODEL_C660) &&
212*4882a593Smuzhiyun (macintosh_config->ident != MAC_MODEL_Q840)) {
213*4882a593Smuzhiyun via_alt_mapping = 1;
214*4882a593Smuzhiyun via1[vDirB] |= 0x40;
215*4882a593Smuzhiyun via1[vBufB] &= ~0x40;
216*4882a593Smuzhiyun } else {
217*4882a593Smuzhiyun via_alt_mapping = 0;
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun
220*4882a593Smuzhiyun /*
221*4882a593Smuzhiyun * Now initialize VIA2. For RBV we just kill all interrupts;
222*4882a593Smuzhiyun * for a regular VIA we also reset the timers and stuff.
223*4882a593Smuzhiyun */
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun via2[gIER] = 0x7F;
226*4882a593Smuzhiyun via2[gIFR] = 0x7F | rbv_clear;
227*4882a593Smuzhiyun if (!rbv_present) {
228*4882a593Smuzhiyun via2[vT1LL] = 0;
229*4882a593Smuzhiyun via2[vT1LH] = 0;
230*4882a593Smuzhiyun via2[vT1CL] = 0;
231*4882a593Smuzhiyun via2[vT1CH] = 0;
232*4882a593Smuzhiyun via2[vT2CL] = 0;
233*4882a593Smuzhiyun via2[vT2CH] = 0;
234*4882a593Smuzhiyun via2[vACR] &= ~0xC0; /* setup T1 timer with no PB7 output */
235*4882a593Smuzhiyun via2[vACR] &= ~0x03; /* disable port A & B latches */
236*4882a593Smuzhiyun }
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun via_nubus_init();
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun /* Everything below this point is VIA2 only... */
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun if (rbv_present)
243*4882a593Smuzhiyun return;
244*4882a593Smuzhiyun
245*4882a593Smuzhiyun /*
246*4882a593Smuzhiyun * Set vPCR for control line interrupts.
247*4882a593Smuzhiyun *
248*4882a593Smuzhiyun * CA1 (SLOTS IRQ), CB1 (ASC IRQ): negative edge trigger.
249*4882a593Smuzhiyun *
250*4882a593Smuzhiyun * Macs with ESP SCSI have a negative edge triggered SCSI interrupt.
251*4882a593Smuzhiyun * Testing reveals that PowerBooks do too. However, the SE/30
252*4882a593Smuzhiyun * schematic diagram shows an active high NCR5380 IRQ line.
253*4882a593Smuzhiyun */
254*4882a593Smuzhiyun
255*4882a593Smuzhiyun pr_debug("VIA2 vPCR is 0x%02X\n", via2[vPCR]);
256*4882a593Smuzhiyun if (macintosh_config->via_type == MAC_VIA_II) {
257*4882a593Smuzhiyun /* CA2 (SCSI DRQ), CB2 (SCSI IRQ): indep. input, pos. edge */
258*4882a593Smuzhiyun via2[vPCR] = 0x66;
259*4882a593Smuzhiyun } else {
260*4882a593Smuzhiyun /* CA2 (SCSI DRQ), CB2 (SCSI IRQ): indep. input, neg. edge */
261*4882a593Smuzhiyun via2[vPCR] = 0x22;
262*4882a593Smuzhiyun }
263*4882a593Smuzhiyun }
264*4882a593Smuzhiyun
265*4882a593Smuzhiyun /*
266*4882a593Smuzhiyun * Debugging dump, used in various places to see what's going on.
267*4882a593Smuzhiyun */
268*4882a593Smuzhiyun
via_debug_dump(void)269*4882a593Smuzhiyun void via_debug_dump(void)
270*4882a593Smuzhiyun {
271*4882a593Smuzhiyun printk(KERN_DEBUG "VIA1: DDRA = 0x%02X DDRB = 0x%02X ACR = 0x%02X\n",
272*4882a593Smuzhiyun (uint) via1[vDirA], (uint) via1[vDirB], (uint) via1[vACR]);
273*4882a593Smuzhiyun printk(KERN_DEBUG " PCR = 0x%02X IFR = 0x%02X IER = 0x%02X\n",
274*4882a593Smuzhiyun (uint) via1[vPCR], (uint) via1[vIFR], (uint) via1[vIER]);
275*4882a593Smuzhiyun if (!via2)
276*4882a593Smuzhiyun return;
277*4882a593Smuzhiyun if (rbv_present) {
278*4882a593Smuzhiyun printk(KERN_DEBUG "VIA2: IFR = 0x%02X IER = 0x%02X\n",
279*4882a593Smuzhiyun (uint) via2[rIFR], (uint) via2[rIER]);
280*4882a593Smuzhiyun printk(KERN_DEBUG " SIFR = 0x%02X SIER = 0x%02X\n",
281*4882a593Smuzhiyun (uint) via2[rSIFR], (uint) via2[rSIER]);
282*4882a593Smuzhiyun } else {
283*4882a593Smuzhiyun printk(KERN_DEBUG "VIA2: DDRA = 0x%02X DDRB = 0x%02X ACR = 0x%02X\n",
284*4882a593Smuzhiyun (uint) via2[vDirA], (uint) via2[vDirB],
285*4882a593Smuzhiyun (uint) via2[vACR]);
286*4882a593Smuzhiyun printk(KERN_DEBUG " PCR = 0x%02X IFR = 0x%02X IER = 0x%02X\n",
287*4882a593Smuzhiyun (uint) via2[vPCR],
288*4882a593Smuzhiyun (uint) via2[vIFR], (uint) via2[vIER]);
289*4882a593Smuzhiyun }
290*4882a593Smuzhiyun }
291*4882a593Smuzhiyun
292*4882a593Smuzhiyun /*
293*4882a593Smuzhiyun * Flush the L2 cache on Macs that have it by flipping
294*4882a593Smuzhiyun * the system into 24-bit mode for an instant.
295*4882a593Smuzhiyun */
296*4882a593Smuzhiyun
via_l2_flush(int writeback)297*4882a593Smuzhiyun void via_l2_flush(int writeback)
298*4882a593Smuzhiyun {
299*4882a593Smuzhiyun unsigned long flags;
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun local_irq_save(flags);
302*4882a593Smuzhiyun via2[gBufB] &= ~VIA2B_vMode32;
303*4882a593Smuzhiyun via2[gBufB] |= VIA2B_vMode32;
304*4882a593Smuzhiyun local_irq_restore(flags);
305*4882a593Smuzhiyun }
306*4882a593Smuzhiyun
307*4882a593Smuzhiyun /*
308*4882a593Smuzhiyun * Return the status of the L2 cache on a IIci
309*4882a593Smuzhiyun */
310*4882a593Smuzhiyun
via_get_cache_disable(void)311*4882a593Smuzhiyun int via_get_cache_disable(void)
312*4882a593Smuzhiyun {
313*4882a593Smuzhiyun /* Safeguard against being called accidentally */
314*4882a593Smuzhiyun if (!via2) {
315*4882a593Smuzhiyun printk(KERN_ERR "via_get_cache_disable called on a non-VIA machine!\n");
316*4882a593Smuzhiyun return 1;
317*4882a593Smuzhiyun }
318*4882a593Smuzhiyun
319*4882a593Smuzhiyun return (int) via2[gBufB] & VIA2B_vCDis;
320*4882a593Smuzhiyun }
321*4882a593Smuzhiyun
322*4882a593Smuzhiyun /*
323*4882a593Smuzhiyun * Initialize VIA2 for Nubus access
324*4882a593Smuzhiyun */
325*4882a593Smuzhiyun
via_nubus_init(void)326*4882a593Smuzhiyun static void __init via_nubus_init(void)
327*4882a593Smuzhiyun {
328*4882a593Smuzhiyun /* unlock nubus transactions */
329*4882a593Smuzhiyun
330*4882a593Smuzhiyun if ((macintosh_config->adb_type != MAC_ADB_PB1) &&
331*4882a593Smuzhiyun (macintosh_config->adb_type != MAC_ADB_PB2)) {
332*4882a593Smuzhiyun /* set the line to be an output on non-RBV machines */
333*4882a593Smuzhiyun if (!rbv_present)
334*4882a593Smuzhiyun via2[vDirB] |= 0x02;
335*4882a593Smuzhiyun
336*4882a593Smuzhiyun /* this seems to be an ADB bit on PMU machines */
337*4882a593Smuzhiyun /* according to MkLinux. -- jmt */
338*4882a593Smuzhiyun via2[gBufB] |= 0x02;
339*4882a593Smuzhiyun }
340*4882a593Smuzhiyun
341*4882a593Smuzhiyun /*
342*4882a593Smuzhiyun * Disable the slot interrupts. On some hardware that's not possible.
343*4882a593Smuzhiyun * On some hardware it's unclear what all of these I/O lines do.
344*4882a593Smuzhiyun */
345*4882a593Smuzhiyun
346*4882a593Smuzhiyun switch (macintosh_config->via_type) {
347*4882a593Smuzhiyun case MAC_VIA_II:
348*4882a593Smuzhiyun case MAC_VIA_QUADRA:
349*4882a593Smuzhiyun pr_debug("VIA2 vDirA is 0x%02X\n", via2[vDirA]);
350*4882a593Smuzhiyun break;
351*4882a593Smuzhiyun case MAC_VIA_IICI:
352*4882a593Smuzhiyun /* RBV. Disable all the slot interrupts. SIER works like IER. */
353*4882a593Smuzhiyun via2[rSIER] = 0x7F;
354*4882a593Smuzhiyun break;
355*4882a593Smuzhiyun }
356*4882a593Smuzhiyun }
357*4882a593Smuzhiyun
via_nubus_irq_startup(int irq)358*4882a593Smuzhiyun void via_nubus_irq_startup(int irq)
359*4882a593Smuzhiyun {
360*4882a593Smuzhiyun int irq_idx = IRQ_IDX(irq);
361*4882a593Smuzhiyun
362*4882a593Smuzhiyun switch (macintosh_config->via_type) {
363*4882a593Smuzhiyun case MAC_VIA_II:
364*4882a593Smuzhiyun case MAC_VIA_QUADRA:
365*4882a593Smuzhiyun /* Make the port A line an input. Probably redundant. */
366*4882a593Smuzhiyun if (macintosh_config->via_type == MAC_VIA_II) {
367*4882a593Smuzhiyun /* The top two bits are RAM size outputs. */
368*4882a593Smuzhiyun via2[vDirA] &= 0xC0 | ~(1 << irq_idx);
369*4882a593Smuzhiyun } else {
370*4882a593Smuzhiyun /* Allow NuBus slots 9 through F. */
371*4882a593Smuzhiyun via2[vDirA] &= 0x80 | ~(1 << irq_idx);
372*4882a593Smuzhiyun }
373*4882a593Smuzhiyun fallthrough;
374*4882a593Smuzhiyun case MAC_VIA_IICI:
375*4882a593Smuzhiyun via_irq_enable(irq);
376*4882a593Smuzhiyun break;
377*4882a593Smuzhiyun }
378*4882a593Smuzhiyun }
379*4882a593Smuzhiyun
via_nubus_irq_shutdown(int irq)380*4882a593Smuzhiyun void via_nubus_irq_shutdown(int irq)
381*4882a593Smuzhiyun {
382*4882a593Smuzhiyun switch (macintosh_config->via_type) {
383*4882a593Smuzhiyun case MAC_VIA_II:
384*4882a593Smuzhiyun case MAC_VIA_QUADRA:
385*4882a593Smuzhiyun /* Ensure that the umbrella CA1 interrupt remains enabled. */
386*4882a593Smuzhiyun via_irq_enable(irq);
387*4882a593Smuzhiyun break;
388*4882a593Smuzhiyun case MAC_VIA_IICI:
389*4882a593Smuzhiyun via_irq_disable(irq);
390*4882a593Smuzhiyun break;
391*4882a593Smuzhiyun }
392*4882a593Smuzhiyun }
393*4882a593Smuzhiyun
394*4882a593Smuzhiyun /*
395*4882a593Smuzhiyun * The generic VIA interrupt routines (shamelessly stolen from Alan Cox's
396*4882a593Smuzhiyun * via6522.c :-), disable/pending masks added.
397*4882a593Smuzhiyun */
398*4882a593Smuzhiyun
399*4882a593Smuzhiyun #define VIA_TIMER_1_INT BIT(6)
400*4882a593Smuzhiyun
via1_irq(struct irq_desc * desc)401*4882a593Smuzhiyun void via1_irq(struct irq_desc *desc)
402*4882a593Smuzhiyun {
403*4882a593Smuzhiyun int irq_num;
404*4882a593Smuzhiyun unsigned char irq_bit, events;
405*4882a593Smuzhiyun
406*4882a593Smuzhiyun events = via1[vIFR] & via1[vIER] & 0x7F;
407*4882a593Smuzhiyun if (!events)
408*4882a593Smuzhiyun return;
409*4882a593Smuzhiyun
410*4882a593Smuzhiyun irq_num = IRQ_MAC_TIMER_1;
411*4882a593Smuzhiyun irq_bit = VIA_TIMER_1_INT;
412*4882a593Smuzhiyun if (events & irq_bit) {
413*4882a593Smuzhiyun unsigned long flags;
414*4882a593Smuzhiyun
415*4882a593Smuzhiyun local_irq_save(flags);
416*4882a593Smuzhiyun via1[vIFR] = irq_bit;
417*4882a593Smuzhiyun generic_handle_irq(irq_num);
418*4882a593Smuzhiyun local_irq_restore(flags);
419*4882a593Smuzhiyun
420*4882a593Smuzhiyun events &= ~irq_bit;
421*4882a593Smuzhiyun if (!events)
422*4882a593Smuzhiyun return;
423*4882a593Smuzhiyun }
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun irq_num = VIA1_SOURCE_BASE;
426*4882a593Smuzhiyun irq_bit = 1;
427*4882a593Smuzhiyun do {
428*4882a593Smuzhiyun if (events & irq_bit) {
429*4882a593Smuzhiyun via1[vIFR] = irq_bit;
430*4882a593Smuzhiyun generic_handle_irq(irq_num);
431*4882a593Smuzhiyun }
432*4882a593Smuzhiyun ++irq_num;
433*4882a593Smuzhiyun irq_bit <<= 1;
434*4882a593Smuzhiyun } while (events >= irq_bit);
435*4882a593Smuzhiyun }
436*4882a593Smuzhiyun
via2_irq(struct irq_desc * desc)437*4882a593Smuzhiyun static void via2_irq(struct irq_desc *desc)
438*4882a593Smuzhiyun {
439*4882a593Smuzhiyun int irq_num;
440*4882a593Smuzhiyun unsigned char irq_bit, events;
441*4882a593Smuzhiyun
442*4882a593Smuzhiyun events = via2[gIFR] & via2[gIER] & 0x7F;
443*4882a593Smuzhiyun if (!events)
444*4882a593Smuzhiyun return;
445*4882a593Smuzhiyun
446*4882a593Smuzhiyun irq_num = VIA2_SOURCE_BASE;
447*4882a593Smuzhiyun irq_bit = 1;
448*4882a593Smuzhiyun do {
449*4882a593Smuzhiyun if (events & irq_bit) {
450*4882a593Smuzhiyun via2[gIFR] = irq_bit | rbv_clear;
451*4882a593Smuzhiyun generic_handle_irq(irq_num);
452*4882a593Smuzhiyun }
453*4882a593Smuzhiyun ++irq_num;
454*4882a593Smuzhiyun irq_bit <<= 1;
455*4882a593Smuzhiyun } while (events >= irq_bit);
456*4882a593Smuzhiyun }
457*4882a593Smuzhiyun
458*4882a593Smuzhiyun /*
459*4882a593Smuzhiyun * Dispatch Nubus interrupts. We are called as a secondary dispatch by the
460*4882a593Smuzhiyun * VIA2 dispatcher as a fast interrupt handler.
461*4882a593Smuzhiyun */
462*4882a593Smuzhiyun
via_nubus_irq(struct irq_desc * desc)463*4882a593Smuzhiyun static void via_nubus_irq(struct irq_desc *desc)
464*4882a593Smuzhiyun {
465*4882a593Smuzhiyun int slot_irq;
466*4882a593Smuzhiyun unsigned char slot_bit, events;
467*4882a593Smuzhiyun
468*4882a593Smuzhiyun events = ~via2[gBufA] & 0x7F;
469*4882a593Smuzhiyun if (rbv_present)
470*4882a593Smuzhiyun events &= via2[rSIER];
471*4882a593Smuzhiyun else
472*4882a593Smuzhiyun events &= ~via2[vDirA];
473*4882a593Smuzhiyun if (!events)
474*4882a593Smuzhiyun return;
475*4882a593Smuzhiyun
476*4882a593Smuzhiyun do {
477*4882a593Smuzhiyun slot_irq = IRQ_NUBUS_F;
478*4882a593Smuzhiyun slot_bit = 0x40;
479*4882a593Smuzhiyun do {
480*4882a593Smuzhiyun if (events & slot_bit) {
481*4882a593Smuzhiyun events &= ~slot_bit;
482*4882a593Smuzhiyun generic_handle_irq(slot_irq);
483*4882a593Smuzhiyun }
484*4882a593Smuzhiyun --slot_irq;
485*4882a593Smuzhiyun slot_bit >>= 1;
486*4882a593Smuzhiyun } while (events);
487*4882a593Smuzhiyun
488*4882a593Smuzhiyun /* clear the CA1 interrupt and make certain there's no more. */
489*4882a593Smuzhiyun via2[gIFR] = 0x02 | rbv_clear;
490*4882a593Smuzhiyun events = ~via2[gBufA] & 0x7F;
491*4882a593Smuzhiyun if (rbv_present)
492*4882a593Smuzhiyun events &= via2[rSIER];
493*4882a593Smuzhiyun else
494*4882a593Smuzhiyun events &= ~via2[vDirA];
495*4882a593Smuzhiyun } while (events);
496*4882a593Smuzhiyun }
497*4882a593Smuzhiyun
498*4882a593Smuzhiyun /*
499*4882a593Smuzhiyun * Register the interrupt dispatchers for VIA or RBV machines only.
500*4882a593Smuzhiyun */
501*4882a593Smuzhiyun
via_register_interrupts(void)502*4882a593Smuzhiyun void __init via_register_interrupts(void)
503*4882a593Smuzhiyun {
504*4882a593Smuzhiyun if (via_alt_mapping) {
505*4882a593Smuzhiyun /* software interrupt */
506*4882a593Smuzhiyun irq_set_chained_handler(IRQ_AUTO_1, via1_irq);
507*4882a593Smuzhiyun /* via1 interrupt */
508*4882a593Smuzhiyun irq_set_chained_handler(IRQ_AUTO_6, via1_irq);
509*4882a593Smuzhiyun } else {
510*4882a593Smuzhiyun irq_set_chained_handler(IRQ_AUTO_1, via1_irq);
511*4882a593Smuzhiyun }
512*4882a593Smuzhiyun irq_set_chained_handler(IRQ_AUTO_2, via2_irq);
513*4882a593Smuzhiyun irq_set_chained_handler(IRQ_MAC_NUBUS, via_nubus_irq);
514*4882a593Smuzhiyun }
515*4882a593Smuzhiyun
via_irq_enable(int irq)516*4882a593Smuzhiyun void via_irq_enable(int irq) {
517*4882a593Smuzhiyun int irq_src = IRQ_SRC(irq);
518*4882a593Smuzhiyun int irq_idx = IRQ_IDX(irq);
519*4882a593Smuzhiyun
520*4882a593Smuzhiyun if (irq_src == 1) {
521*4882a593Smuzhiyun via1[vIER] = IER_SET_BIT(irq_idx);
522*4882a593Smuzhiyun } else if (irq_src == 2) {
523*4882a593Smuzhiyun if (irq != IRQ_MAC_NUBUS || nubus_disabled == 0)
524*4882a593Smuzhiyun via2[gIER] = IER_SET_BIT(irq_idx);
525*4882a593Smuzhiyun } else if (irq_src == 7) {
526*4882a593Smuzhiyun switch (macintosh_config->via_type) {
527*4882a593Smuzhiyun case MAC_VIA_II:
528*4882a593Smuzhiyun case MAC_VIA_QUADRA:
529*4882a593Smuzhiyun nubus_disabled &= ~(1 << irq_idx);
530*4882a593Smuzhiyun /* Enable the CA1 interrupt when no slot is disabled. */
531*4882a593Smuzhiyun if (!nubus_disabled)
532*4882a593Smuzhiyun via2[gIER] = IER_SET_BIT(1);
533*4882a593Smuzhiyun break;
534*4882a593Smuzhiyun case MAC_VIA_IICI:
535*4882a593Smuzhiyun /* On RBV, enable the slot interrupt.
536*4882a593Smuzhiyun * SIER works like IER.
537*4882a593Smuzhiyun */
538*4882a593Smuzhiyun via2[rSIER] = IER_SET_BIT(irq_idx);
539*4882a593Smuzhiyun break;
540*4882a593Smuzhiyun }
541*4882a593Smuzhiyun }
542*4882a593Smuzhiyun }
543*4882a593Smuzhiyun
via_irq_disable(int irq)544*4882a593Smuzhiyun void via_irq_disable(int irq) {
545*4882a593Smuzhiyun int irq_src = IRQ_SRC(irq);
546*4882a593Smuzhiyun int irq_idx = IRQ_IDX(irq);
547*4882a593Smuzhiyun
548*4882a593Smuzhiyun if (irq_src == 1) {
549*4882a593Smuzhiyun via1[vIER] = IER_CLR_BIT(irq_idx);
550*4882a593Smuzhiyun } else if (irq_src == 2) {
551*4882a593Smuzhiyun via2[gIER] = IER_CLR_BIT(irq_idx);
552*4882a593Smuzhiyun } else if (irq_src == 7) {
553*4882a593Smuzhiyun switch (macintosh_config->via_type) {
554*4882a593Smuzhiyun case MAC_VIA_II:
555*4882a593Smuzhiyun case MAC_VIA_QUADRA:
556*4882a593Smuzhiyun nubus_disabled |= 1 << irq_idx;
557*4882a593Smuzhiyun if (nubus_disabled)
558*4882a593Smuzhiyun via2[gIER] = IER_CLR_BIT(1);
559*4882a593Smuzhiyun break;
560*4882a593Smuzhiyun case MAC_VIA_IICI:
561*4882a593Smuzhiyun via2[rSIER] = IER_CLR_BIT(irq_idx);
562*4882a593Smuzhiyun break;
563*4882a593Smuzhiyun }
564*4882a593Smuzhiyun }
565*4882a593Smuzhiyun }
566*4882a593Smuzhiyun
via1_set_head(int head)567*4882a593Smuzhiyun void via1_set_head(int head)
568*4882a593Smuzhiyun {
569*4882a593Smuzhiyun if (head == 0)
570*4882a593Smuzhiyun via1[vBufA] &= ~VIA1A_vHeadSel;
571*4882a593Smuzhiyun else
572*4882a593Smuzhiyun via1[vBufA] |= VIA1A_vHeadSel;
573*4882a593Smuzhiyun }
574*4882a593Smuzhiyun EXPORT_SYMBOL(via1_set_head);
575*4882a593Smuzhiyun
via2_scsi_drq_pending(void)576*4882a593Smuzhiyun int via2_scsi_drq_pending(void)
577*4882a593Smuzhiyun {
578*4882a593Smuzhiyun return via2[gIFR] & (1 << IRQ_IDX(IRQ_MAC_SCSIDRQ));
579*4882a593Smuzhiyun }
580*4882a593Smuzhiyun EXPORT_SYMBOL(via2_scsi_drq_pending);
581*4882a593Smuzhiyun
582*4882a593Smuzhiyun /* timer and clock source */
583*4882a593Smuzhiyun
584*4882a593Smuzhiyun #define VIA_CLOCK_FREQ 783360 /* VIA "phase 2" clock in Hz */
585*4882a593Smuzhiyun #define VIA_TIMER_CYCLES (VIA_CLOCK_FREQ / HZ) /* clock cycles per jiffy */
586*4882a593Smuzhiyun
587*4882a593Smuzhiyun #define VIA_TC (VIA_TIMER_CYCLES - 2) /* including 0 and -1 */
588*4882a593Smuzhiyun #define VIA_TC_LOW (VIA_TC & 0xFF)
589*4882a593Smuzhiyun #define VIA_TC_HIGH (VIA_TC >> 8)
590*4882a593Smuzhiyun
591*4882a593Smuzhiyun static u64 mac_read_clk(struct clocksource *cs);
592*4882a593Smuzhiyun
593*4882a593Smuzhiyun static struct clocksource mac_clk = {
594*4882a593Smuzhiyun .name = "via1",
595*4882a593Smuzhiyun .rating = 250,
596*4882a593Smuzhiyun .read = mac_read_clk,
597*4882a593Smuzhiyun .mask = CLOCKSOURCE_MASK(32),
598*4882a593Smuzhiyun .flags = CLOCK_SOURCE_IS_CONTINUOUS,
599*4882a593Smuzhiyun };
600*4882a593Smuzhiyun
601*4882a593Smuzhiyun static u32 clk_total, clk_offset;
602*4882a593Smuzhiyun
via_timer_handler(int irq,void * dev_id)603*4882a593Smuzhiyun static irqreturn_t via_timer_handler(int irq, void *dev_id)
604*4882a593Smuzhiyun {
605*4882a593Smuzhiyun irq_handler_t timer_routine = dev_id;
606*4882a593Smuzhiyun
607*4882a593Smuzhiyun clk_total += VIA_TIMER_CYCLES;
608*4882a593Smuzhiyun clk_offset = 0;
609*4882a593Smuzhiyun timer_routine(0, NULL);
610*4882a593Smuzhiyun
611*4882a593Smuzhiyun return IRQ_HANDLED;
612*4882a593Smuzhiyun }
613*4882a593Smuzhiyun
via_init_clock(irq_handler_t timer_routine)614*4882a593Smuzhiyun void __init via_init_clock(irq_handler_t timer_routine)
615*4882a593Smuzhiyun {
616*4882a593Smuzhiyun if (request_irq(IRQ_MAC_TIMER_1, via_timer_handler, IRQF_TIMER, "timer",
617*4882a593Smuzhiyun timer_routine)) {
618*4882a593Smuzhiyun pr_err("Couldn't register %s interrupt\n", "timer");
619*4882a593Smuzhiyun return;
620*4882a593Smuzhiyun }
621*4882a593Smuzhiyun
622*4882a593Smuzhiyun via1[vT1LL] = VIA_TC_LOW;
623*4882a593Smuzhiyun via1[vT1LH] = VIA_TC_HIGH;
624*4882a593Smuzhiyun via1[vT1CL] = VIA_TC_LOW;
625*4882a593Smuzhiyun via1[vT1CH] = VIA_TC_HIGH;
626*4882a593Smuzhiyun via1[vACR] |= 0x40;
627*4882a593Smuzhiyun
628*4882a593Smuzhiyun clocksource_register_hz(&mac_clk, VIA_CLOCK_FREQ);
629*4882a593Smuzhiyun }
630*4882a593Smuzhiyun
mac_read_clk(struct clocksource * cs)631*4882a593Smuzhiyun static u64 mac_read_clk(struct clocksource *cs)
632*4882a593Smuzhiyun {
633*4882a593Smuzhiyun unsigned long flags;
634*4882a593Smuzhiyun u8 count_high;
635*4882a593Smuzhiyun u16 count;
636*4882a593Smuzhiyun u32 ticks;
637*4882a593Smuzhiyun
638*4882a593Smuzhiyun /*
639*4882a593Smuzhiyun * Timer counter wrap-around is detected with the timer interrupt flag
640*4882a593Smuzhiyun * but reading the counter low byte (vT1CL) would reset the flag.
641*4882a593Smuzhiyun * Also, accessing both counter registers is essentially a data race.
642*4882a593Smuzhiyun * These problems are avoided by ignoring the low byte. Clock accuracy
643*4882a593Smuzhiyun * is 256 times worse (error can reach 0.327 ms) but CPU overhead is
644*4882a593Smuzhiyun * reduced by avoiding slow VIA register accesses.
645*4882a593Smuzhiyun */
646*4882a593Smuzhiyun
647*4882a593Smuzhiyun local_irq_save(flags);
648*4882a593Smuzhiyun count_high = via1[vT1CH];
649*4882a593Smuzhiyun if (count_high == 0xFF)
650*4882a593Smuzhiyun count_high = 0;
651*4882a593Smuzhiyun if (count_high > 0 && (via1[vIFR] & VIA_TIMER_1_INT))
652*4882a593Smuzhiyun clk_offset = VIA_TIMER_CYCLES;
653*4882a593Smuzhiyun count = count_high << 8;
654*4882a593Smuzhiyun ticks = VIA_TIMER_CYCLES - count;
655*4882a593Smuzhiyun ticks += clk_offset + clk_total;
656*4882a593Smuzhiyun local_irq_restore(flags);
657*4882a593Smuzhiyun
658*4882a593Smuzhiyun return ticks;
659*4882a593Smuzhiyun }
660