1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * 6522 Versatile Interface Adapter (VIA)
41da177e4SLinus Torvalds *
50c79cf6aSSimon Arlott * There are two of these on the Mac II. Some IRQs are vectored
61da177e4SLinus Torvalds * via them as are assorted bits and bobs - eg RTC, ADB.
71da177e4SLinus Torvalds *
81da177e4SLinus Torvalds * CSA: Motorola seems to have removed documentation on the 6522 from
91da177e4SLinus Torvalds * their web site; try
101da177e4SLinus Torvalds * http://nerini.drf.com/vectrex/other/text/chips/6522/
111da177e4SLinus Torvalds * http://www.zymurgy.net/classic/vic20/vicdet1.htm
121da177e4SLinus Torvalds * and
131da177e4SLinus Torvalds * http://193.23.168.87/mikro_laborversuche/via_iobaustein/via6522_1.html
141da177e4SLinus Torvalds * for info. A full-text web search on 6522 AND VIA will probably also
151da177e4SLinus Torvalds * net some usefulness. <cananian@alumni.princeton.edu> 20apr1999
161da177e4SLinus Torvalds *
1767dfb153SFinn Thain * Additional data is here (the SY6522 was used in the Mac II etc):
1867dfb153SFinn Thain * http://www.6502.org/documents/datasheets/synertek/synertek_sy6522.pdf
1967dfb153SFinn Thain * http://www.6502.org/documents/datasheets/synertek/synertek_sy6522_programming_reference.pdf
2067dfb153SFinn Thain *
211da177e4SLinus Torvalds * PRAM/RTC access algorithms are from the NetBSD RTC toolkit version 1.08b
221da177e4SLinus Torvalds * by Erik Vogan and adapted to Linux by Joshua M. Thompson (funaho@jurai.org)
231da177e4SLinus Torvalds *
241da177e4SLinus Torvalds */
251da177e4SLinus Torvalds
26481fa139SFinn Thain #include <linux/clocksource.h>
271da177e4SLinus Torvalds #include <linux/types.h>
281da177e4SLinus Torvalds #include <linux/kernel.h>
291da177e4SLinus Torvalds #include <linux/mm.h>
301da177e4SLinus Torvalds #include <linux/delay.h>
311da177e4SLinus Torvalds #include <linux/init.h>
32deea7775SAdrian Bunk #include <linux/module.h>
33ddc7fd25SGeert Uytterhoeven #include <linux/irq.h>
341da177e4SLinus Torvalds
351da177e4SLinus Torvalds #include <asm/macintosh.h>
361da177e4SLinus Torvalds #include <asm/macints.h>
371da177e4SLinus Torvalds #include <asm/mac_via.h>
381da177e4SLinus Torvalds #include <asm/mac_psc.h>
39c85627fbSGeert Uytterhoeven #include <asm/mac_oss.h>
401da177e4SLinus Torvalds
411da177e4SLinus Torvalds volatile __u8 *via1, *via2;
42deea7775SAdrian Bunk int rbv_present;
43deea7775SAdrian Bunk int via_alt_mapping;
44deea7775SAdrian Bunk EXPORT_SYMBOL(via_alt_mapping);
458dfbdf4aSAdrian Bunk static __u8 rbv_clear;
461da177e4SLinus Torvalds
471da177e4SLinus Torvalds /*
481da177e4SLinus Torvalds * Globals for accessing the VIA chip registers without having to
491da177e4SLinus Torvalds * check if we're hitting a real VIA or an RBV. Normally you could
501da177e4SLinus Torvalds * just hit the combined register (ie, vIER|rIER) but that seems to
511da177e4SLinus Torvalds * break on AV Macs...probably because they actually decode more than
521da177e4SLinus Torvalds * eight address bits. Why can't Apple engineers at least be
531da177e4SLinus Torvalds * _consistently_ lazy? - 1999-05-21 (jmt)
541da177e4SLinus Torvalds */
551da177e4SLinus Torvalds
561da177e4SLinus Torvalds static int gIER,gIFR,gBufA,gBufB;
571da177e4SLinus Torvalds
581da177e4SLinus Torvalds /*
59c4af5da7SFinn Thain * On Macs with a genuine VIA chip there is no way to mask an individual slot
60c4af5da7SFinn Thain * interrupt. This limitation also seems to apply to VIA clone logic cores in
61c4af5da7SFinn Thain * Quadra-like ASICs. (RBV and OSS machines don't have this limitation.)
62c4af5da7SFinn Thain *
63efbec135SAdam Buchbinder * We used to fake it by configuring the relevant VIA pin as an output
64c4af5da7SFinn Thain * (to mask the interrupt) or input (to unmask). That scheme did not work on
65c4af5da7SFinn Thain * (at least) the Quadra 700. A NuBus card's /NMRQ signal is an open-collector
66c4af5da7SFinn Thain * circuit (see Designing Cards and Drivers for Macintosh II and Macintosh SE,
67c4af5da7SFinn Thain * p. 10-11 etc) but VIA outputs are not (see datasheet).
68c4af5da7SFinn Thain *
69c4af5da7SFinn Thain * Driving these outputs high must cause the VIA to source current and the
70c4af5da7SFinn Thain * card to sink current when it asserts /NMRQ. Current will flow but the pin
71c4af5da7SFinn Thain * voltage is uncertain and so the /NMRQ condition may still cause a transition
72c4af5da7SFinn Thain * at the VIA2 CA1 input (which explains the lost interrupts). A side effect
73c4af5da7SFinn Thain * is that a disabled slot IRQ can never be tested as pending or not.
74c4af5da7SFinn Thain *
75c4af5da7SFinn Thain * Driving these outputs low doesn't work either. All the slot /NMRQ lines are
76c4af5da7SFinn Thain * (active low) OR'd together to generate the CA1 (aka "SLOTS") interrupt (see
77c4af5da7SFinn Thain * The Guide To Macintosh Family Hardware, 2nd edition p. 167). If we drive a
78c4af5da7SFinn Thain * disabled /NMRQ line low, the falling edge immediately triggers a CA1
79c4af5da7SFinn Thain * interrupt and all slot interrupts after that will generate no transition
80c4af5da7SFinn Thain * and therefore no interrupt, even after being re-enabled.
81c4af5da7SFinn Thain *
82c4af5da7SFinn Thain * So we make the VIA port A I/O lines inputs and use nubus_disabled to keep
83c4af5da7SFinn Thain * track of their states. When any slot IRQ becomes disabled we mask the CA1
84c4af5da7SFinn Thain * umbrella interrupt. Only when all slot IRQs become enabled do we unmask
85c4af5da7SFinn Thain * the CA1 interrupt. It must remain enabled even when cards have no interrupt
86c4af5da7SFinn Thain * handler registered. Drivers must therefore disable a slot interrupt at the
87c4af5da7SFinn Thain * device before they call free_irq (like shared and autovector interrupts).
88c4af5da7SFinn Thain *
89c4af5da7SFinn Thain * There is also a related problem when MacOS is used to boot Linux. A network
90c4af5da7SFinn Thain * card brought up by a MacOS driver may raise an interrupt while Linux boots.
91c4af5da7SFinn Thain * This can be fatal since it can't be handled until the right driver loads
92c4af5da7SFinn Thain * (if such a driver exists at all). Apparently related to this hardware
93c4af5da7SFinn Thain * limitation, "Designing Cards and Drivers", p. 9-8, says that a slot
94c4af5da7SFinn Thain * interrupt with no driver would crash MacOS (the book was written before
95c4af5da7SFinn Thain * the appearance of Macs with RBV or OSS).
96cd713ddcSFinn Thain */
97c4af5da7SFinn Thain
98cd713ddcSFinn Thain static u8 nubus_disabled;
991da177e4SLinus Torvalds
1001da177e4SLinus Torvalds void via_debug_dump(void);
1018ee90c5cSFinn Thain static void via_nubus_init(void);
1021da177e4SLinus Torvalds
1031da177e4SLinus Torvalds /*
1041da177e4SLinus Torvalds * Initialize the VIAs
1051da177e4SLinus Torvalds *
1061da177e4SLinus Torvalds * First we figure out where they actually _are_ as well as what type of
1071da177e4SLinus Torvalds * VIA we have for VIA2 (it could be a real VIA or an RBV or even an OSS.)
1081da177e4SLinus Torvalds * Then we pretty much clear them out and disable all IRQ sources.
1091da177e4SLinus Torvalds */
1101da177e4SLinus Torvalds
via_init(void)1111da177e4SLinus Torvalds void __init via_init(void)
1121da177e4SLinus Torvalds {
1130e37a23eSFinn Thain via1 = (void *)VIA1_BASE;
1140e37a23eSFinn Thain pr_debug("VIA1 detected at %p\n", via1);
1150e37a23eSFinn Thain
1167a0bb442SFinn Thain if (oss_present) {
1177a0bb442SFinn Thain via2 = NULL;
1187a0bb442SFinn Thain rbv_present = 0;
1197a0bb442SFinn Thain } else {
1201da177e4SLinus Torvalds switch (macintosh_config->via_type) {
1211da177e4SLinus Torvalds
1221da177e4SLinus Torvalds /* IIci, IIsi, IIvx, IIvi (P6xx), LC series */
1231da177e4SLinus Torvalds
124608e287bSFinn Thain case MAC_VIA_IICI:
1251da177e4SLinus Torvalds via2 = (void *)RBV_BASE;
1260e37a23eSFinn Thain pr_debug("VIA2 (RBV) detected at %p\n", via2);
1271da177e4SLinus Torvalds rbv_present = 1;
1281da177e4SLinus Torvalds if (macintosh_config->ident == MAC_MODEL_LCIII) {
1291da177e4SLinus Torvalds rbv_clear = 0x00;
1301da177e4SLinus Torvalds } else {
1311da177e4SLinus Torvalds /* on most RBVs (& unlike the VIAs), you */
1321da177e4SLinus Torvalds /* need to set bit 7 when you write to IFR */
1331da177e4SLinus Torvalds /* in order for your clear to occur. */
1341da177e4SLinus Torvalds rbv_clear = 0x80;
1351da177e4SLinus Torvalds }
1361da177e4SLinus Torvalds gIER = rIER;
1371da177e4SLinus Torvalds gIFR = rIFR;
1381da177e4SLinus Torvalds gBufA = rSIFR;
1391da177e4SLinus Torvalds gBufB = rBufB;
1401da177e4SLinus Torvalds break;
1411da177e4SLinus Torvalds
1421da177e4SLinus Torvalds /* Quadra and early MacIIs agree on the VIA locations */
1431da177e4SLinus Torvalds
1441da177e4SLinus Torvalds case MAC_VIA_QUADRA:
1451da177e4SLinus Torvalds case MAC_VIA_II:
1461da177e4SLinus Torvalds via2 = (void *) VIA2_BASE;
1470e37a23eSFinn Thain pr_debug("VIA2 detected at %p\n", via2);
1481da177e4SLinus Torvalds rbv_present = 0;
1491da177e4SLinus Torvalds rbv_clear = 0x00;
1501da177e4SLinus Torvalds gIER = vIER;
1511da177e4SLinus Torvalds gIFR = vIFR;
1521da177e4SLinus Torvalds gBufA = vBufA;
1531da177e4SLinus Torvalds gBufB = vBufB;
1541da177e4SLinus Torvalds break;
1557a0bb442SFinn Thain
1561da177e4SLinus Torvalds default:
1571da177e4SLinus Torvalds panic("UNKNOWN VIA TYPE");
1581da177e4SLinus Torvalds }
1591da177e4SLinus Torvalds }
1601da177e4SLinus Torvalds
1611da177e4SLinus Torvalds #ifdef DEBUG_VIA
1621da177e4SLinus Torvalds via_debug_dump();
1631da177e4SLinus Torvalds #endif
1641da177e4SLinus Torvalds
1651da177e4SLinus Torvalds /*
1661da177e4SLinus Torvalds * Shut down all IRQ sources, reset the timers, and
1671da177e4SLinus Torvalds * kill the timer latch on VIA1.
1681da177e4SLinus Torvalds */
1691da177e4SLinus Torvalds
1701da177e4SLinus Torvalds via1[vIER] = 0x7F;
1711da177e4SLinus Torvalds via1[vIFR] = 0x7F;
1721da177e4SLinus Torvalds via1[vT1CL] = 0;
1731da177e4SLinus Torvalds via1[vT1CH] = 0;
1741da177e4SLinus Torvalds via1[vT2CL] = 0;
1751da177e4SLinus Torvalds via1[vT2CH] = 0;
1764a973592SFinn Thain via1[vACR] &= ~0xC0; /* setup T1 timer with no PB7 output */
17767dfb153SFinn Thain via1[vACR] &= ~0x03; /* disable port A & B latches */
1781da177e4SLinus Torvalds
1791da177e4SLinus Torvalds /*
1801da177e4SLinus Torvalds * SE/30: disable video IRQ
1811da177e4SLinus Torvalds */
1821da177e4SLinus Torvalds
1831da177e4SLinus Torvalds if (macintosh_config->ident == MAC_MODEL_SE30) {
1841da177e4SLinus Torvalds via1[vDirB] |= 0x40;
1851da177e4SLinus Torvalds via1[vBufB] |= 0x40;
1861da177e4SLinus Torvalds }
1871da177e4SLinus Torvalds
1886a93207bSFinn Thain switch (macintosh_config->adb_type) {
1896a93207bSFinn Thain case MAC_ADB_IOP:
1906a93207bSFinn Thain case MAC_ADB_II:
1916a93207bSFinn Thain case MAC_ADB_PB1:
1921da177e4SLinus Torvalds /*
1931da177e4SLinus Torvalds * Set the RTC bits to a known state: all lines to outputs and
1941da177e4SLinus Torvalds * RTC disabled (yes that's 0 to enable and 1 to disable).
1951da177e4SLinus Torvalds */
1966a93207bSFinn Thain via1[vDirB] |= VIA1B_vRTCEnb | VIA1B_vRTCClk | VIA1B_vRTCData;
1976a93207bSFinn Thain via1[vBufB] |= VIA1B_vRTCEnb | VIA1B_vRTCClk;
1986a93207bSFinn Thain break;
1996a93207bSFinn Thain }
2001da177e4SLinus Torvalds
2011da177e4SLinus Torvalds /* Everything below this point is VIA2/RBV only... */
2021da177e4SLinus Torvalds
2034a973592SFinn Thain if (oss_present)
2044a973592SFinn Thain return;
2051da177e4SLinus Torvalds
2068d9f014aSFinn Thain if ((macintosh_config->via_type == MAC_VIA_QUADRA) &&
2078d9f014aSFinn Thain (macintosh_config->adb_type != MAC_ADB_PB1) &&
2088d9f014aSFinn Thain (macintosh_config->adb_type != MAC_ADB_PB2) &&
2098d9f014aSFinn Thain (macintosh_config->ident != MAC_MODEL_C660) &&
2108d9f014aSFinn Thain (macintosh_config->ident != MAC_MODEL_Q840)) {
2111da177e4SLinus Torvalds via_alt_mapping = 1;
2121da177e4SLinus Torvalds via1[vDirB] |= 0x40;
2131da177e4SLinus Torvalds via1[vBufB] &= ~0x40;
2148d9f014aSFinn Thain } else {
2158d9f014aSFinn Thain via_alt_mapping = 0;
2161da177e4SLinus Torvalds }
2171da177e4SLinus Torvalds
2181da177e4SLinus Torvalds /*
2191da177e4SLinus Torvalds * Now initialize VIA2. For RBV we just kill all interrupts;
2201da177e4SLinus Torvalds * for a regular VIA we also reset the timers and stuff.
2211da177e4SLinus Torvalds */
2221da177e4SLinus Torvalds
2231da177e4SLinus Torvalds via2[gIER] = 0x7F;
2241da177e4SLinus Torvalds via2[gIFR] = 0x7F | rbv_clear;
2251da177e4SLinus Torvalds if (!rbv_present) {
2261da177e4SLinus Torvalds via2[vT1CL] = 0;
2271da177e4SLinus Torvalds via2[vT1CH] = 0;
2281da177e4SLinus Torvalds via2[vT2CL] = 0;
2291da177e4SLinus Torvalds via2[vT2CH] = 0;
2304a973592SFinn Thain via2[vACR] &= ~0xC0; /* setup T1 timer with no PB7 output */
23167dfb153SFinn Thain via2[vACR] &= ~0x03; /* disable port A & B latches */
23267dfb153SFinn Thain }
23367dfb153SFinn Thain
2348ee90c5cSFinn Thain via_nubus_init();
2358ee90c5cSFinn Thain
236aa8a9fbeSFinn Thain /* Everything below this point is VIA2 only... */
237aa8a9fbeSFinn Thain
238aa8a9fbeSFinn Thain if (rbv_present)
239aa8a9fbeSFinn Thain return;
240aa8a9fbeSFinn Thain
24167dfb153SFinn Thain /*
242aa8a9fbeSFinn Thain * Set vPCR for control line interrupts.
243aa8a9fbeSFinn Thain *
244aa8a9fbeSFinn Thain * CA1 (SLOTS IRQ), CB1 (ASC IRQ): negative edge trigger.
245aa8a9fbeSFinn Thain *
246aa8a9fbeSFinn Thain * Macs with ESP SCSI have a negative edge triggered SCSI interrupt.
247aa8a9fbeSFinn Thain * Testing reveals that PowerBooks do too. However, the SE/30
248aa8a9fbeSFinn Thain * schematic diagram shows an active high NCR5380 IRQ line.
24967dfb153SFinn Thain */
250aa8a9fbeSFinn Thain
251aa8a9fbeSFinn Thain pr_debug("VIA2 vPCR is 0x%02X\n", via2[vPCR]);
252aa8a9fbeSFinn Thain if (macintosh_config->via_type == MAC_VIA_II) {
253aa8a9fbeSFinn Thain /* CA2 (SCSI DRQ), CB2 (SCSI IRQ): indep. input, pos. edge */
25467dfb153SFinn Thain via2[vPCR] = 0x66;
25567dfb153SFinn Thain } else {
256aa8a9fbeSFinn Thain /* CA2 (SCSI DRQ), CB2 (SCSI IRQ): indep. input, neg. edge */
25767dfb153SFinn Thain via2[vPCR] = 0x22;
25867dfb153SFinn Thain }
2591da177e4SLinus Torvalds }
2601da177e4SLinus Torvalds
2611da177e4SLinus Torvalds /*
2621da177e4SLinus Torvalds * Debugging dump, used in various places to see what's going on.
2631da177e4SLinus Torvalds */
2641da177e4SLinus Torvalds
via_debug_dump(void)2651da177e4SLinus Torvalds void via_debug_dump(void)
2661da177e4SLinus Torvalds {
2671da177e4SLinus Torvalds printk(KERN_DEBUG "VIA1: DDRA = 0x%02X DDRB = 0x%02X ACR = 0x%02X\n",
2681da177e4SLinus Torvalds (uint) via1[vDirA], (uint) via1[vDirB], (uint) via1[vACR]);
2691da177e4SLinus Torvalds printk(KERN_DEBUG " PCR = 0x%02X IFR = 0x%02X IER = 0x%02X\n",
2701da177e4SLinus Torvalds (uint) via1[vPCR], (uint) via1[vIFR], (uint) via1[vIER]);
2717a0bb442SFinn Thain if (!via2)
2727a0bb442SFinn Thain return;
2737a0bb442SFinn Thain if (rbv_present) {
2741da177e4SLinus Torvalds printk(KERN_DEBUG "VIA2: IFR = 0x%02X IER = 0x%02X\n",
2751da177e4SLinus Torvalds (uint) via2[rIFR], (uint) via2[rIER]);
2761da177e4SLinus Torvalds printk(KERN_DEBUG " SIFR = 0x%02X SIER = 0x%02X\n",
2771da177e4SLinus Torvalds (uint) via2[rSIFR], (uint) via2[rSIER]);
2781da177e4SLinus Torvalds } else {
2791da177e4SLinus Torvalds printk(KERN_DEBUG "VIA2: DDRA = 0x%02X DDRB = 0x%02X ACR = 0x%02X\n",
2801da177e4SLinus Torvalds (uint) via2[vDirA], (uint) via2[vDirB],
2811da177e4SLinus Torvalds (uint) via2[vACR]);
2821da177e4SLinus Torvalds printk(KERN_DEBUG " PCR = 0x%02X IFR = 0x%02X IER = 0x%02X\n",
2831da177e4SLinus Torvalds (uint) via2[vPCR],
2841da177e4SLinus Torvalds (uint) via2[vIFR], (uint) via2[vIER]);
2851da177e4SLinus Torvalds }
2861da177e4SLinus Torvalds }
2871da177e4SLinus Torvalds
2881da177e4SLinus Torvalds /*
2891da177e4SLinus Torvalds * Flush the L2 cache on Macs that have it by flipping
2901da177e4SLinus Torvalds * the system into 24-bit mode for an instant.
2911da177e4SLinus Torvalds */
2921da177e4SLinus Torvalds
via_l2_flush(int writeback)293bcc44f6bSFinn Thain void via_l2_flush(int writeback)
2941da177e4SLinus Torvalds {
295bcc44f6bSFinn Thain unsigned long flags;
296bcc44f6bSFinn Thain
297bcc44f6bSFinn Thain local_irq_save(flags);
2981da177e4SLinus Torvalds via2[gBufB] &= ~VIA2B_vMode32;
2991da177e4SLinus Torvalds via2[gBufB] |= VIA2B_vMode32;
300bcc44f6bSFinn Thain local_irq_restore(flags);
3011da177e4SLinus Torvalds }
3021da177e4SLinus Torvalds
3031da177e4SLinus Torvalds /*
3041da177e4SLinus Torvalds * Initialize VIA2 for Nubus access
3051da177e4SLinus Torvalds */
3061da177e4SLinus Torvalds
via_nubus_init(void)3078ee90c5cSFinn Thain static void __init via_nubus_init(void)
3081da177e4SLinus Torvalds {
3091da177e4SLinus Torvalds /* unlock nubus transactions */
3101da177e4SLinus Torvalds
3111da177e4SLinus Torvalds if ((macintosh_config->adb_type != MAC_ADB_PB1) &&
3121da177e4SLinus Torvalds (macintosh_config->adb_type != MAC_ADB_PB2)) {
31367dfb153SFinn Thain /* set the line to be an output on non-RBV machines */
31467dfb153SFinn Thain if (!rbv_present)
3151da177e4SLinus Torvalds via2[vDirB] |= 0x02;
3161da177e4SLinus Torvalds
3171da177e4SLinus Torvalds /* this seems to be an ADB bit on PMU machines */
3181da177e4SLinus Torvalds /* according to MkLinux. -- jmt */
3191da177e4SLinus Torvalds via2[gBufB] |= 0x02;
3201da177e4SLinus Torvalds }
3211da177e4SLinus Torvalds
322c4af5da7SFinn Thain /*
323c4af5da7SFinn Thain * Disable the slot interrupts. On some hardware that's not possible.
324c4af5da7SFinn Thain * On some hardware it's unclear what all of these I/O lines do.
325c4af5da7SFinn Thain */
326cd713ddcSFinn Thain
327cd713ddcSFinn Thain switch (macintosh_config->via_type) {
328cd713ddcSFinn Thain case MAC_VIA_II:
329c4af5da7SFinn Thain case MAC_VIA_QUADRA:
330c4af5da7SFinn Thain pr_debug("VIA2 vDirA is 0x%02X\n", via2[vDirA]);
331cd713ddcSFinn Thain break;
332608e287bSFinn Thain case MAC_VIA_IICI:
333cd713ddcSFinn Thain /* RBV. Disable all the slot interrupts. SIER works like IER. */
334cd713ddcSFinn Thain via2[rSIER] = 0x7F;
335cd713ddcSFinn Thain break;
3361da177e4SLinus Torvalds }
337c4af5da7SFinn Thain }
338c4af5da7SFinn Thain
via_nubus_irq_startup(int irq)339c4af5da7SFinn Thain void via_nubus_irq_startup(int irq)
340c4af5da7SFinn Thain {
341c4af5da7SFinn Thain int irq_idx = IRQ_IDX(irq);
342c4af5da7SFinn Thain
343c4af5da7SFinn Thain switch (macintosh_config->via_type) {
344c4af5da7SFinn Thain case MAC_VIA_II:
345c4af5da7SFinn Thain case MAC_VIA_QUADRA:
346c4af5da7SFinn Thain /* Make the port A line an input. Probably redundant. */
347c4af5da7SFinn Thain if (macintosh_config->via_type == MAC_VIA_II) {
348c4af5da7SFinn Thain /* The top two bits are RAM size outputs. */
349c4af5da7SFinn Thain via2[vDirA] &= 0xC0 | ~(1 << irq_idx);
350c4af5da7SFinn Thain } else {
351c4af5da7SFinn Thain /* Allow NuBus slots 9 through F. */
352c4af5da7SFinn Thain via2[vDirA] &= 0x80 | ~(1 << irq_idx);
353c4af5da7SFinn Thain }
354df561f66SGustavo A. R. Silva fallthrough;
355608e287bSFinn Thain case MAC_VIA_IICI:
356c4af5da7SFinn Thain via_irq_enable(irq);
357c4af5da7SFinn Thain break;
358c4af5da7SFinn Thain }
359c4af5da7SFinn Thain }
360c4af5da7SFinn Thain
via_nubus_irq_shutdown(int irq)361c4af5da7SFinn Thain void via_nubus_irq_shutdown(int irq)
362c4af5da7SFinn Thain {
363c4af5da7SFinn Thain switch (macintosh_config->via_type) {
364c4af5da7SFinn Thain case MAC_VIA_II:
365c4af5da7SFinn Thain case MAC_VIA_QUADRA:
366c4af5da7SFinn Thain /* Ensure that the umbrella CA1 interrupt remains enabled. */
367c4af5da7SFinn Thain via_irq_enable(irq);
368c4af5da7SFinn Thain break;
369608e287bSFinn Thain case MAC_VIA_IICI:
370c4af5da7SFinn Thain via_irq_disable(irq);
371cd713ddcSFinn Thain break;
3721da177e4SLinus Torvalds }
3731da177e4SLinus Torvalds }
3741da177e4SLinus Torvalds
3751da177e4SLinus Torvalds /*
3761da177e4SLinus Torvalds * The generic VIA interrupt routines (shamelessly stolen from Alan Cox's
3771da177e4SLinus Torvalds * via6522.c :-), disable/pending masks added.
3781da177e4SLinus Torvalds */
3791da177e4SLinus Torvalds
3801efdd4bdSFinn Thain #define VIA_TIMER_1_INT BIT(6)
3811efdd4bdSFinn Thain
via1_irq(struct irq_desc * desc)382bd0b9ac4SThomas Gleixner void via1_irq(struct irq_desc *desc)
3839145db56SGeert Uytterhoeven {
3849145db56SGeert Uytterhoeven int irq_num;
3859145db56SGeert Uytterhoeven unsigned char irq_bit, events;
3869145db56SGeert Uytterhoeven
3879145db56SGeert Uytterhoeven events = via1[vIFR] & via1[vIER] & 0x7F;
3889145db56SGeert Uytterhoeven if (!events)
3899145db56SGeert Uytterhoeven return;
3909145db56SGeert Uytterhoeven
3911efdd4bdSFinn Thain irq_num = IRQ_MAC_TIMER_1;
3921efdd4bdSFinn Thain irq_bit = VIA_TIMER_1_INT;
3931efdd4bdSFinn Thain if (events & irq_bit) {
3941efdd4bdSFinn Thain unsigned long flags;
3951efdd4bdSFinn Thain
3961efdd4bdSFinn Thain local_irq_save(flags);
3971efdd4bdSFinn Thain via1[vIFR] = irq_bit;
3981efdd4bdSFinn Thain generic_handle_irq(irq_num);
3991efdd4bdSFinn Thain local_irq_restore(flags);
4001efdd4bdSFinn Thain
4011efdd4bdSFinn Thain events &= ~irq_bit;
4021efdd4bdSFinn Thain if (!events)
4031efdd4bdSFinn Thain return;
4041efdd4bdSFinn Thain }
4051efdd4bdSFinn Thain
4069145db56SGeert Uytterhoeven irq_num = VIA1_SOURCE_BASE;
4079145db56SGeert Uytterhoeven irq_bit = 1;
4089145db56SGeert Uytterhoeven do {
4099145db56SGeert Uytterhoeven if (events & irq_bit) {
4109145db56SGeert Uytterhoeven via1[vIFR] = irq_bit;
4119145db56SGeert Uytterhoeven generic_handle_irq(irq_num);
4129145db56SGeert Uytterhoeven }
4139145db56SGeert Uytterhoeven ++irq_num;
4149145db56SGeert Uytterhoeven irq_bit <<= 1;
4159145db56SGeert Uytterhoeven } while (events >= irq_bit);
4169145db56SGeert Uytterhoeven }
4179145db56SGeert Uytterhoeven
via2_irq(struct irq_desc * desc)418bd0b9ac4SThomas Gleixner static void via2_irq(struct irq_desc *desc)
4199145db56SGeert Uytterhoeven {
4209145db56SGeert Uytterhoeven int irq_num;
4219145db56SGeert Uytterhoeven unsigned char irq_bit, events;
4229145db56SGeert Uytterhoeven
4239145db56SGeert Uytterhoeven events = via2[gIFR] & via2[gIER] & 0x7F;
4249145db56SGeert Uytterhoeven if (!events)
4259145db56SGeert Uytterhoeven return;
4269145db56SGeert Uytterhoeven
4279145db56SGeert Uytterhoeven irq_num = VIA2_SOURCE_BASE;
4289145db56SGeert Uytterhoeven irq_bit = 1;
4299145db56SGeert Uytterhoeven do {
4309145db56SGeert Uytterhoeven if (events & irq_bit) {
4319145db56SGeert Uytterhoeven via2[gIFR] = irq_bit | rbv_clear;
4329145db56SGeert Uytterhoeven generic_handle_irq(irq_num);
4339145db56SGeert Uytterhoeven }
4349145db56SGeert Uytterhoeven ++irq_num;
4359145db56SGeert Uytterhoeven irq_bit <<= 1;
4369145db56SGeert Uytterhoeven } while (events >= irq_bit);
4379145db56SGeert Uytterhoeven }
4381da177e4SLinus Torvalds
4391da177e4SLinus Torvalds /*
4401da177e4SLinus Torvalds * Dispatch Nubus interrupts. We are called as a secondary dispatch by the
4411da177e4SLinus Torvalds * VIA2 dispatcher as a fast interrupt handler.
4421da177e4SLinus Torvalds */
4431da177e4SLinus Torvalds
via_nubus_irq(struct irq_desc * desc)444bd0b9ac4SThomas Gleixner static void via_nubus_irq(struct irq_desc *desc)
4459145db56SGeert Uytterhoeven {
4469145db56SGeert Uytterhoeven int slot_irq;
4479145db56SGeert Uytterhoeven unsigned char slot_bit, events;
4489145db56SGeert Uytterhoeven
4499145db56SGeert Uytterhoeven events = ~via2[gBufA] & 0x7F;
4509145db56SGeert Uytterhoeven if (rbv_present)
4519145db56SGeert Uytterhoeven events &= via2[rSIER];
4529145db56SGeert Uytterhoeven else
4539145db56SGeert Uytterhoeven events &= ~via2[vDirA];
4549145db56SGeert Uytterhoeven if (!events)
4559145db56SGeert Uytterhoeven return;
4569145db56SGeert Uytterhoeven
4579145db56SGeert Uytterhoeven do {
4589145db56SGeert Uytterhoeven slot_irq = IRQ_NUBUS_F;
4599145db56SGeert Uytterhoeven slot_bit = 0x40;
4609145db56SGeert Uytterhoeven do {
4619145db56SGeert Uytterhoeven if (events & slot_bit) {
4629145db56SGeert Uytterhoeven events &= ~slot_bit;
4639145db56SGeert Uytterhoeven generic_handle_irq(slot_irq);
4649145db56SGeert Uytterhoeven }
4659145db56SGeert Uytterhoeven --slot_irq;
4669145db56SGeert Uytterhoeven slot_bit >>= 1;
4679145db56SGeert Uytterhoeven } while (events);
4689145db56SGeert Uytterhoeven
4699145db56SGeert Uytterhoeven /* clear the CA1 interrupt and make certain there's no more. */
4709145db56SGeert Uytterhoeven via2[gIFR] = 0x02 | rbv_clear;
4719145db56SGeert Uytterhoeven events = ~via2[gBufA] & 0x7F;
4729145db56SGeert Uytterhoeven if (rbv_present)
4739145db56SGeert Uytterhoeven events &= via2[rSIER];
4749145db56SGeert Uytterhoeven else
4759145db56SGeert Uytterhoeven events &= ~via2[vDirA];
4769145db56SGeert Uytterhoeven } while (events);
4779145db56SGeert Uytterhoeven }
4789145db56SGeert Uytterhoeven
4799145db56SGeert Uytterhoeven /*
4809145db56SGeert Uytterhoeven * Register the interrupt dispatchers for VIA or RBV machines only.
4819145db56SGeert Uytterhoeven */
4829145db56SGeert Uytterhoeven
via_register_interrupts(void)4839145db56SGeert Uytterhoeven void __init via_register_interrupts(void)
4849145db56SGeert Uytterhoeven {
4859145db56SGeert Uytterhoeven if (via_alt_mapping) {
4869145db56SGeert Uytterhoeven /* software interrupt */
4879145db56SGeert Uytterhoeven irq_set_chained_handler(IRQ_AUTO_1, via1_irq);
4889145db56SGeert Uytterhoeven /* via1 interrupt */
4899145db56SGeert Uytterhoeven irq_set_chained_handler(IRQ_AUTO_6, via1_irq);
4909145db56SGeert Uytterhoeven } else {
4919145db56SGeert Uytterhoeven irq_set_chained_handler(IRQ_AUTO_1, via1_irq);
4929145db56SGeert Uytterhoeven }
4939145db56SGeert Uytterhoeven irq_set_chained_handler(IRQ_AUTO_2, via2_irq);
4949145db56SGeert Uytterhoeven irq_set_chained_handler(IRQ_MAC_NUBUS, via_nubus_irq);
4959145db56SGeert Uytterhoeven }
4961da177e4SLinus Torvalds
via_irq_enable(int irq)4971da177e4SLinus Torvalds void via_irq_enable(int irq) {
4981da177e4SLinus Torvalds int irq_src = IRQ_SRC(irq);
4991da177e4SLinus Torvalds int irq_idx = IRQ_IDX(irq);
5001da177e4SLinus Torvalds
5011da177e4SLinus Torvalds if (irq_src == 1) {
502cd713ddcSFinn Thain via1[vIER] = IER_SET_BIT(irq_idx);
5031da177e4SLinus Torvalds } else if (irq_src == 2) {
504cd713ddcSFinn Thain if (irq != IRQ_MAC_NUBUS || nubus_disabled == 0)
505cd713ddcSFinn Thain via2[gIER] = IER_SET_BIT(irq_idx);
5061da177e4SLinus Torvalds } else if (irq_src == 7) {
507cd713ddcSFinn Thain switch (macintosh_config->via_type) {
508cd713ddcSFinn Thain case MAC_VIA_II:
509c4af5da7SFinn Thain case MAC_VIA_QUADRA:
510cd713ddcSFinn Thain nubus_disabled &= ~(1 << irq_idx);
511cd713ddcSFinn Thain /* Enable the CA1 interrupt when no slot is disabled. */
512cd713ddcSFinn Thain if (!nubus_disabled)
513cd713ddcSFinn Thain via2[gIER] = IER_SET_BIT(1);
5141da177e4SLinus Torvalds break;
515608e287bSFinn Thain case MAC_VIA_IICI:
516cd713ddcSFinn Thain /* On RBV, enable the slot interrupt.
517cd713ddcSFinn Thain * SIER works like IER.
518cd713ddcSFinn Thain */
519cd713ddcSFinn Thain via2[rSIER] = IER_SET_BIT(irq_idx);
520cd713ddcSFinn Thain break;
5211da177e4SLinus Torvalds }
5221da177e4SLinus Torvalds }
5231da177e4SLinus Torvalds }
5241da177e4SLinus Torvalds
via_irq_disable(int irq)5251da177e4SLinus Torvalds void via_irq_disable(int irq) {
5261da177e4SLinus Torvalds int irq_src = IRQ_SRC(irq);
5271da177e4SLinus Torvalds int irq_idx = IRQ_IDX(irq);
5281da177e4SLinus Torvalds
5291da177e4SLinus Torvalds if (irq_src == 1) {
530cd713ddcSFinn Thain via1[vIER] = IER_CLR_BIT(irq_idx);
5311da177e4SLinus Torvalds } else if (irq_src == 2) {
532cd713ddcSFinn Thain via2[gIER] = IER_CLR_BIT(irq_idx);
5331da177e4SLinus Torvalds } else if (irq_src == 7) {
534cd713ddcSFinn Thain switch (macintosh_config->via_type) {
535cd713ddcSFinn Thain case MAC_VIA_II:
536c4af5da7SFinn Thain case MAC_VIA_QUADRA:
537cd713ddcSFinn Thain nubus_disabled |= 1 << irq_idx;
538cd713ddcSFinn Thain if (nubus_disabled)
539cd713ddcSFinn Thain via2[gIER] = IER_CLR_BIT(1);
540cd713ddcSFinn Thain break;
541608e287bSFinn Thain case MAC_VIA_IICI:
5421da177e4SLinus Torvalds via2[rSIER] = IER_CLR_BIT(irq_idx);
543cd713ddcSFinn Thain break;
5441da177e4SLinus Torvalds }
5451da177e4SLinus Torvalds }
5461da177e4SLinus Torvalds }
5471da177e4SLinus Torvalds
via1_set_head(int head)5488852ecd9SLaurent Vivier void via1_set_head(int head)
5498852ecd9SLaurent Vivier {
5508852ecd9SLaurent Vivier if (head == 0)
5518852ecd9SLaurent Vivier via1[vBufA] &= ~VIA1A_vHeadSel;
5528852ecd9SLaurent Vivier else
5538852ecd9SLaurent Vivier via1[vBufA] |= VIA1A_vHeadSel;
5548852ecd9SLaurent Vivier }
5558852ecd9SLaurent Vivier EXPORT_SYMBOL(via1_set_head);
55630c0527dSFinn Thain
via2_scsi_drq_pending(void)55730c0527dSFinn Thain int via2_scsi_drq_pending(void)
55830c0527dSFinn Thain {
55930c0527dSFinn Thain return via2[gIFR] & (1 << IRQ_IDX(IRQ_MAC_SCSIDRQ));
56030c0527dSFinn Thain }
56130c0527dSFinn Thain EXPORT_SYMBOL(via2_scsi_drq_pending);
5620ca7ce7dSFinn Thain
5630ca7ce7dSFinn Thain /* timer and clock source */
5640ca7ce7dSFinn Thain
5650ca7ce7dSFinn Thain #define VIA_CLOCK_FREQ 783360 /* VIA "phase 2" clock in Hz */
5660ca7ce7dSFinn Thain #define VIA_TIMER_CYCLES (VIA_CLOCK_FREQ / HZ) /* clock cycles per jiffy */
5670ca7ce7dSFinn Thain
5680ca7ce7dSFinn Thain #define VIA_TC (VIA_TIMER_CYCLES - 2) /* including 0 and -1 */
5690ca7ce7dSFinn Thain #define VIA_TC_LOW (VIA_TC & 0xFF)
5700ca7ce7dSFinn Thain #define VIA_TC_HIGH (VIA_TC >> 8)
5710ca7ce7dSFinn Thain
572481fa139SFinn Thain static u64 mac_read_clk(struct clocksource *cs);
573481fa139SFinn Thain
574481fa139SFinn Thain static struct clocksource mac_clk = {
575481fa139SFinn Thain .name = "via1",
576481fa139SFinn Thain .rating = 250,
577481fa139SFinn Thain .read = mac_read_clk,
578481fa139SFinn Thain .mask = CLOCKSOURCE_MASK(32),
579481fa139SFinn Thain .flags = CLOCK_SOURCE_IS_CONTINUOUS,
580481fa139SFinn Thain };
581481fa139SFinn Thain
582481fa139SFinn Thain static u32 clk_total, clk_offset;
583481fa139SFinn Thain
via_timer_handler(int irq,void * dev_id)584481fa139SFinn Thain static irqreturn_t via_timer_handler(int irq, void *dev_id)
585481fa139SFinn Thain {
586481fa139SFinn Thain clk_total += VIA_TIMER_CYCLES;
587481fa139SFinn Thain clk_offset = 0;
58842f1d57fSArnd Bergmann legacy_timer_tick(1);
589481fa139SFinn Thain
590481fa139SFinn Thain return IRQ_HANDLED;
591481fa139SFinn Thain }
592481fa139SFinn Thain
via_init_clock(void)593*f9a01539SArnd Bergmann void __init via_init_clock(void)
5940ca7ce7dSFinn Thain {
595481fa139SFinn Thain if (request_irq(IRQ_MAC_TIMER_1, via_timer_handler, IRQF_TIMER, "timer",
59642f1d57fSArnd Bergmann NULL)) {
5970ca7ce7dSFinn Thain pr_err("Couldn't register %s interrupt\n", "timer");
5980ca7ce7dSFinn Thain return;
5990ca7ce7dSFinn Thain }
6000ca7ce7dSFinn Thain
6010ca7ce7dSFinn Thain via1[vT1CL] = VIA_TC_LOW;
6020ca7ce7dSFinn Thain via1[vT1CH] = VIA_TC_HIGH;
6030ca7ce7dSFinn Thain via1[vACR] |= 0x40;
604481fa139SFinn Thain
605481fa139SFinn Thain clocksource_register_hz(&mac_clk, VIA_CLOCK_FREQ);
6060ca7ce7dSFinn Thain }
6070ca7ce7dSFinn Thain
mac_read_clk(struct clocksource * cs)608481fa139SFinn Thain static u64 mac_read_clk(struct clocksource *cs)
6090ca7ce7dSFinn Thain {
6100ca7ce7dSFinn Thain unsigned long flags;
6110ca7ce7dSFinn Thain u8 count_high;
612481fa139SFinn Thain u16 count;
613481fa139SFinn Thain u32 ticks;
6140ca7ce7dSFinn Thain
6150ca7ce7dSFinn Thain /*
6160ca7ce7dSFinn Thain * Timer counter wrap-around is detected with the timer interrupt flag
6170ca7ce7dSFinn Thain * but reading the counter low byte (vT1CL) would reset the flag.
6180ca7ce7dSFinn Thain * Also, accessing both counter registers is essentially a data race.
6190ca7ce7dSFinn Thain * These problems are avoided by ignoring the low byte. Clock accuracy
6200ca7ce7dSFinn Thain * is 256 times worse (error can reach 0.327 ms) but CPU overhead is
6210ca7ce7dSFinn Thain * reduced by avoiding slow VIA register accesses.
6220ca7ce7dSFinn Thain */
6230ca7ce7dSFinn Thain
6240ca7ce7dSFinn Thain local_irq_save(flags);
6250ca7ce7dSFinn Thain count_high = via1[vT1CH];
6260ca7ce7dSFinn Thain if (count_high == 0xFF)
6270ca7ce7dSFinn Thain count_high = 0;
6280ca7ce7dSFinn Thain if (count_high > 0 && (via1[vIFR] & VIA_TIMER_1_INT))
629481fa139SFinn Thain clk_offset = VIA_TIMER_CYCLES;
630481fa139SFinn Thain count = count_high << 8;
631481fa139SFinn Thain ticks = VIA_TIMER_CYCLES - count;
632481fa139SFinn Thain ticks += clk_offset + clk_total;
6330ca7ce7dSFinn Thain local_irq_restore(flags);
6340ca7ce7dSFinn Thain
635481fa139SFinn Thain return ticks;
6360ca7ce7dSFinn Thain }
637