162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *	6522 Versatile Interface Adapter (VIA)
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *	There are two of these on the Mac II. Some IRQs are vectored
662306a36Sopenharmony_ci *	via them as are assorted bits and bobs - eg RTC, ADB.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * CSA: Motorola seems to have removed documentation on the 6522 from
962306a36Sopenharmony_ci * their web site; try
1062306a36Sopenharmony_ci *     http://nerini.drf.com/vectrex/other/text/chips/6522/
1162306a36Sopenharmony_ci *     http://www.zymurgy.net/classic/vic20/vicdet1.htm
1262306a36Sopenharmony_ci * and
1362306a36Sopenharmony_ci *     http://193.23.168.87/mikro_laborversuche/via_iobaustein/via6522_1.html
1462306a36Sopenharmony_ci * for info.  A full-text web search on 6522 AND VIA will probably also
1562306a36Sopenharmony_ci * net some usefulness. <cananian@alumni.princeton.edu> 20apr1999
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci * Additional data is here (the SY6522 was used in the Mac II etc):
1862306a36Sopenharmony_ci *     http://www.6502.org/documents/datasheets/synertek/synertek_sy6522.pdf
1962306a36Sopenharmony_ci *     http://www.6502.org/documents/datasheets/synertek/synertek_sy6522_programming_reference.pdf
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci * PRAM/RTC access algorithms are from the NetBSD RTC toolkit version 1.08b
2262306a36Sopenharmony_ci * by Erik Vogan and adapted to Linux by Joshua M. Thompson (funaho@jurai.org)
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include <linux/clocksource.h>
2762306a36Sopenharmony_ci#include <linux/types.h>
2862306a36Sopenharmony_ci#include <linux/kernel.h>
2962306a36Sopenharmony_ci#include <linux/mm.h>
3062306a36Sopenharmony_ci#include <linux/delay.h>
3162306a36Sopenharmony_ci#include <linux/init.h>
3262306a36Sopenharmony_ci#include <linux/module.h>
3362306a36Sopenharmony_ci#include <linux/irq.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include <asm/macintosh.h>
3662306a36Sopenharmony_ci#include <asm/macints.h>
3762306a36Sopenharmony_ci#include <asm/mac_via.h>
3862306a36Sopenharmony_ci#include <asm/mac_psc.h>
3962306a36Sopenharmony_ci#include <asm/mac_oss.h>
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_civolatile __u8 *via1, *via2;
4262306a36Sopenharmony_ciint rbv_present;
4362306a36Sopenharmony_ciint via_alt_mapping;
4462306a36Sopenharmony_ciEXPORT_SYMBOL(via_alt_mapping);
4562306a36Sopenharmony_cistatic __u8 rbv_clear;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/*
4862306a36Sopenharmony_ci * Globals for accessing the VIA chip registers without having to
4962306a36Sopenharmony_ci * check if we're hitting a real VIA or an RBV. Normally you could
5062306a36Sopenharmony_ci * just hit the combined register (ie, vIER|rIER) but that seems to
5162306a36Sopenharmony_ci * break on AV Macs...probably because they actually decode more than
5262306a36Sopenharmony_ci * eight address bits. Why can't Apple engineers at least be
5362306a36Sopenharmony_ci * _consistently_ lazy?                          - 1999-05-21 (jmt)
5462306a36Sopenharmony_ci */
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic int gIER,gIFR,gBufA,gBufB;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/*
5962306a36Sopenharmony_ci * On Macs with a genuine VIA chip there is no way to mask an individual slot
6062306a36Sopenharmony_ci * interrupt. This limitation also seems to apply to VIA clone logic cores in
6162306a36Sopenharmony_ci * Quadra-like ASICs. (RBV and OSS machines don't have this limitation.)
6262306a36Sopenharmony_ci *
6362306a36Sopenharmony_ci * We used to fake it by configuring the relevant VIA pin as an output
6462306a36Sopenharmony_ci * (to mask the interrupt) or input (to unmask). That scheme did not work on
6562306a36Sopenharmony_ci * (at least) the Quadra 700. A NuBus card's /NMRQ signal is an open-collector
6662306a36Sopenharmony_ci * circuit (see Designing Cards and Drivers for Macintosh II and Macintosh SE,
6762306a36Sopenharmony_ci * p. 10-11 etc) but VIA outputs are not (see datasheet).
6862306a36Sopenharmony_ci *
6962306a36Sopenharmony_ci * Driving these outputs high must cause the VIA to source current and the
7062306a36Sopenharmony_ci * card to sink current when it asserts /NMRQ. Current will flow but the pin
7162306a36Sopenharmony_ci * voltage is uncertain and so the /NMRQ condition may still cause a transition
7262306a36Sopenharmony_ci * at the VIA2 CA1 input (which explains the lost interrupts). A side effect
7362306a36Sopenharmony_ci * is that a disabled slot IRQ can never be tested as pending or not.
7462306a36Sopenharmony_ci *
7562306a36Sopenharmony_ci * Driving these outputs low doesn't work either. All the slot /NMRQ lines are
7662306a36Sopenharmony_ci * (active low) OR'd together to generate the CA1 (aka "SLOTS") interrupt (see
7762306a36Sopenharmony_ci * The Guide To Macintosh Family Hardware, 2nd edition p. 167). If we drive a
7862306a36Sopenharmony_ci * disabled /NMRQ line low, the falling edge immediately triggers a CA1
7962306a36Sopenharmony_ci * interrupt and all slot interrupts after that will generate no transition
8062306a36Sopenharmony_ci * and therefore no interrupt, even after being re-enabled.
8162306a36Sopenharmony_ci *
8262306a36Sopenharmony_ci * So we make the VIA port A I/O lines inputs and use nubus_disabled to keep
8362306a36Sopenharmony_ci * track of their states. When any slot IRQ becomes disabled we mask the CA1
8462306a36Sopenharmony_ci * umbrella interrupt. Only when all slot IRQs become enabled do we unmask
8562306a36Sopenharmony_ci * the CA1 interrupt. It must remain enabled even when cards have no interrupt
8662306a36Sopenharmony_ci * handler registered. Drivers must therefore disable a slot interrupt at the
8762306a36Sopenharmony_ci * device before they call free_irq (like shared and autovector interrupts).
8862306a36Sopenharmony_ci *
8962306a36Sopenharmony_ci * There is also a related problem when MacOS is used to boot Linux. A network
9062306a36Sopenharmony_ci * card brought up by a MacOS driver may raise an interrupt while Linux boots.
9162306a36Sopenharmony_ci * This can be fatal since it can't be handled until the right driver loads
9262306a36Sopenharmony_ci * (if such a driver exists at all). Apparently related to this hardware
9362306a36Sopenharmony_ci * limitation, "Designing Cards and Drivers", p. 9-8, says that a slot
9462306a36Sopenharmony_ci * interrupt with no driver would crash MacOS (the book was written before
9562306a36Sopenharmony_ci * the appearance of Macs with RBV or OSS).
9662306a36Sopenharmony_ci */
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic u8 nubus_disabled;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_civoid via_debug_dump(void);
10162306a36Sopenharmony_cistatic void via_nubus_init(void);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci/*
10462306a36Sopenharmony_ci * Initialize the VIAs
10562306a36Sopenharmony_ci *
10662306a36Sopenharmony_ci * First we figure out where they actually _are_ as well as what type of
10762306a36Sopenharmony_ci * VIA we have for VIA2 (it could be a real VIA or an RBV or even an OSS.)
10862306a36Sopenharmony_ci * Then we pretty much clear them out and disable all IRQ sources.
10962306a36Sopenharmony_ci */
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_civoid __init via_init(void)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	via1 = (void *)VIA1_BASE;
11462306a36Sopenharmony_ci	pr_debug("VIA1 detected at %p\n", via1);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (oss_present) {
11762306a36Sopenharmony_ci		via2 = NULL;
11862306a36Sopenharmony_ci		rbv_present = 0;
11962306a36Sopenharmony_ci	} else {
12062306a36Sopenharmony_ci		switch (macintosh_config->via_type) {
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci		/* IIci, IIsi, IIvx, IIvi (P6xx), LC series */
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci		case MAC_VIA_IICI:
12562306a36Sopenharmony_ci			via2 = (void *)RBV_BASE;
12662306a36Sopenharmony_ci			pr_debug("VIA2 (RBV) detected at %p\n", via2);
12762306a36Sopenharmony_ci			rbv_present = 1;
12862306a36Sopenharmony_ci			if (macintosh_config->ident == MAC_MODEL_LCIII) {
12962306a36Sopenharmony_ci				rbv_clear = 0x00;
13062306a36Sopenharmony_ci			} else {
13162306a36Sopenharmony_ci				/* on most RBVs (& unlike the VIAs), you   */
13262306a36Sopenharmony_ci				/* need to set bit 7 when you write to IFR */
13362306a36Sopenharmony_ci				/* in order for your clear to occur.       */
13462306a36Sopenharmony_ci				rbv_clear = 0x80;
13562306a36Sopenharmony_ci			}
13662306a36Sopenharmony_ci			gIER = rIER;
13762306a36Sopenharmony_ci			gIFR = rIFR;
13862306a36Sopenharmony_ci			gBufA = rSIFR;
13962306a36Sopenharmony_ci			gBufB = rBufB;
14062306a36Sopenharmony_ci			break;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci		/* Quadra and early MacIIs agree on the VIA locations */
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci		case MAC_VIA_QUADRA:
14562306a36Sopenharmony_ci		case MAC_VIA_II:
14662306a36Sopenharmony_ci			via2 = (void *) VIA2_BASE;
14762306a36Sopenharmony_ci			pr_debug("VIA2 detected at %p\n", via2);
14862306a36Sopenharmony_ci			rbv_present = 0;
14962306a36Sopenharmony_ci			rbv_clear = 0x00;
15062306a36Sopenharmony_ci			gIER = vIER;
15162306a36Sopenharmony_ci			gIFR = vIFR;
15262306a36Sopenharmony_ci			gBufA = vBufA;
15362306a36Sopenharmony_ci			gBufB = vBufB;
15462306a36Sopenharmony_ci			break;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci		default:
15762306a36Sopenharmony_ci			panic("UNKNOWN VIA TYPE");
15862306a36Sopenharmony_ci		}
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci#ifdef DEBUG_VIA
16262306a36Sopenharmony_ci	via_debug_dump();
16362306a36Sopenharmony_ci#endif
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	/*
16662306a36Sopenharmony_ci	 * Shut down all IRQ sources, reset the timers, and
16762306a36Sopenharmony_ci	 * kill the timer latch on VIA1.
16862306a36Sopenharmony_ci	 */
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	via1[vIER] = 0x7F;
17162306a36Sopenharmony_ci	via1[vIFR] = 0x7F;
17262306a36Sopenharmony_ci	via1[vT1CL] = 0;
17362306a36Sopenharmony_ci	via1[vT1CH] = 0;
17462306a36Sopenharmony_ci	via1[vT2CL] = 0;
17562306a36Sopenharmony_ci	via1[vT2CH] = 0;
17662306a36Sopenharmony_ci	via1[vACR] &= ~0xC0; /* setup T1 timer with no PB7 output */
17762306a36Sopenharmony_ci	via1[vACR] &= ~0x03; /* disable port A & B latches */
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	/*
18062306a36Sopenharmony_ci	 * SE/30: disable video IRQ
18162306a36Sopenharmony_ci	 */
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	if (macintosh_config->ident == MAC_MODEL_SE30) {
18462306a36Sopenharmony_ci		via1[vDirB] |= 0x40;
18562306a36Sopenharmony_ci		via1[vBufB] |= 0x40;
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	switch (macintosh_config->adb_type) {
18962306a36Sopenharmony_ci	case MAC_ADB_IOP:
19062306a36Sopenharmony_ci	case MAC_ADB_II:
19162306a36Sopenharmony_ci	case MAC_ADB_PB1:
19262306a36Sopenharmony_ci		/*
19362306a36Sopenharmony_ci		 * Set the RTC bits to a known state: all lines to outputs and
19462306a36Sopenharmony_ci		 * RTC disabled (yes that's 0 to enable and 1 to disable).
19562306a36Sopenharmony_ci		 */
19662306a36Sopenharmony_ci		via1[vDirB] |= VIA1B_vRTCEnb | VIA1B_vRTCClk | VIA1B_vRTCData;
19762306a36Sopenharmony_ci		via1[vBufB] |= VIA1B_vRTCEnb | VIA1B_vRTCClk;
19862306a36Sopenharmony_ci		break;
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	/* Everything below this point is VIA2/RBV only... */
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	if (oss_present)
20462306a36Sopenharmony_ci		return;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	if ((macintosh_config->via_type == MAC_VIA_QUADRA) &&
20762306a36Sopenharmony_ci	    (macintosh_config->adb_type != MAC_ADB_PB1) &&
20862306a36Sopenharmony_ci	    (macintosh_config->adb_type != MAC_ADB_PB2) &&
20962306a36Sopenharmony_ci	    (macintosh_config->ident    != MAC_MODEL_C660) &&
21062306a36Sopenharmony_ci	    (macintosh_config->ident    != MAC_MODEL_Q840)) {
21162306a36Sopenharmony_ci		via_alt_mapping = 1;
21262306a36Sopenharmony_ci		via1[vDirB] |= 0x40;
21362306a36Sopenharmony_ci		via1[vBufB] &= ~0x40;
21462306a36Sopenharmony_ci	} else {
21562306a36Sopenharmony_ci		via_alt_mapping = 0;
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	/*
21962306a36Sopenharmony_ci	 * Now initialize VIA2. For RBV we just kill all interrupts;
22062306a36Sopenharmony_ci	 * for a regular VIA we also reset the timers and stuff.
22162306a36Sopenharmony_ci	 */
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	via2[gIER] = 0x7F;
22462306a36Sopenharmony_ci	via2[gIFR] = 0x7F | rbv_clear;
22562306a36Sopenharmony_ci	if (!rbv_present) {
22662306a36Sopenharmony_ci		via2[vT1CL] = 0;
22762306a36Sopenharmony_ci		via2[vT1CH] = 0;
22862306a36Sopenharmony_ci		via2[vT2CL] = 0;
22962306a36Sopenharmony_ci		via2[vT2CH] = 0;
23062306a36Sopenharmony_ci		via2[vACR] &= ~0xC0; /* setup T1 timer with no PB7 output */
23162306a36Sopenharmony_ci		via2[vACR] &= ~0x03; /* disable port A & B latches */
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	via_nubus_init();
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	/* Everything below this point is VIA2 only... */
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	if (rbv_present)
23962306a36Sopenharmony_ci		return;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/*
24262306a36Sopenharmony_ci	 * Set vPCR for control line interrupts.
24362306a36Sopenharmony_ci	 *
24462306a36Sopenharmony_ci	 * CA1 (SLOTS IRQ), CB1 (ASC IRQ): negative edge trigger.
24562306a36Sopenharmony_ci	 *
24662306a36Sopenharmony_ci	 * Macs with ESP SCSI have a negative edge triggered SCSI interrupt.
24762306a36Sopenharmony_ci	 * Testing reveals that PowerBooks do too. However, the SE/30
24862306a36Sopenharmony_ci	 * schematic diagram shows an active high NCR5380 IRQ line.
24962306a36Sopenharmony_ci	 */
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	pr_debug("VIA2 vPCR is 0x%02X\n", via2[vPCR]);
25262306a36Sopenharmony_ci	if (macintosh_config->via_type == MAC_VIA_II) {
25362306a36Sopenharmony_ci		/* CA2 (SCSI DRQ), CB2 (SCSI IRQ): indep. input, pos. edge */
25462306a36Sopenharmony_ci		via2[vPCR] = 0x66;
25562306a36Sopenharmony_ci	} else {
25662306a36Sopenharmony_ci		/* CA2 (SCSI DRQ), CB2 (SCSI IRQ): indep. input, neg. edge */
25762306a36Sopenharmony_ci		via2[vPCR] = 0x22;
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci/*
26262306a36Sopenharmony_ci * Debugging dump, used in various places to see what's going on.
26362306a36Sopenharmony_ci */
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_civoid via_debug_dump(void)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	printk(KERN_DEBUG "VIA1: DDRA = 0x%02X DDRB = 0x%02X ACR = 0x%02X\n",
26862306a36Sopenharmony_ci		(uint) via1[vDirA], (uint) via1[vDirB], (uint) via1[vACR]);
26962306a36Sopenharmony_ci	printk(KERN_DEBUG "         PCR = 0x%02X  IFR = 0x%02X IER = 0x%02X\n",
27062306a36Sopenharmony_ci		(uint) via1[vPCR], (uint) via1[vIFR], (uint) via1[vIER]);
27162306a36Sopenharmony_ci	if (!via2)
27262306a36Sopenharmony_ci		return;
27362306a36Sopenharmony_ci	if (rbv_present) {
27462306a36Sopenharmony_ci		printk(KERN_DEBUG "VIA2:  IFR = 0x%02X  IER = 0x%02X\n",
27562306a36Sopenharmony_ci			(uint) via2[rIFR], (uint) via2[rIER]);
27662306a36Sopenharmony_ci		printk(KERN_DEBUG "      SIFR = 0x%02X SIER = 0x%02X\n",
27762306a36Sopenharmony_ci			(uint) via2[rSIFR], (uint) via2[rSIER]);
27862306a36Sopenharmony_ci	} else {
27962306a36Sopenharmony_ci		printk(KERN_DEBUG "VIA2: DDRA = 0x%02X DDRB = 0x%02X ACR = 0x%02X\n",
28062306a36Sopenharmony_ci			(uint) via2[vDirA], (uint) via2[vDirB],
28162306a36Sopenharmony_ci			(uint) via2[vACR]);
28262306a36Sopenharmony_ci		printk(KERN_DEBUG "         PCR = 0x%02X  IFR = 0x%02X IER = 0x%02X\n",
28362306a36Sopenharmony_ci			(uint) via2[vPCR],
28462306a36Sopenharmony_ci			(uint) via2[vIFR], (uint) via2[vIER]);
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci/*
28962306a36Sopenharmony_ci * Flush the L2 cache on Macs that have it by flipping
29062306a36Sopenharmony_ci * the system into 24-bit mode for an instant.
29162306a36Sopenharmony_ci */
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_civoid via_l2_flush(int writeback)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	unsigned long flags;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	local_irq_save(flags);
29862306a36Sopenharmony_ci	via2[gBufB] &= ~VIA2B_vMode32;
29962306a36Sopenharmony_ci	via2[gBufB] |= VIA2B_vMode32;
30062306a36Sopenharmony_ci	local_irq_restore(flags);
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci/*
30462306a36Sopenharmony_ci * Initialize VIA2 for Nubus access
30562306a36Sopenharmony_ci */
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic void __init via_nubus_init(void)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	/* unlock nubus transactions */
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	if ((macintosh_config->adb_type != MAC_ADB_PB1) &&
31262306a36Sopenharmony_ci	    (macintosh_config->adb_type != MAC_ADB_PB2)) {
31362306a36Sopenharmony_ci		/* set the line to be an output on non-RBV machines */
31462306a36Sopenharmony_ci		if (!rbv_present)
31562306a36Sopenharmony_ci			via2[vDirB] |= 0x02;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci		/* this seems to be an ADB bit on PMU machines */
31862306a36Sopenharmony_ci		/* according to MkLinux.  -- jmt               */
31962306a36Sopenharmony_ci		via2[gBufB] |= 0x02;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	/*
32362306a36Sopenharmony_ci	 * Disable the slot interrupts. On some hardware that's not possible.
32462306a36Sopenharmony_ci	 * On some hardware it's unclear what all of these I/O lines do.
32562306a36Sopenharmony_ci	 */
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	switch (macintosh_config->via_type) {
32862306a36Sopenharmony_ci	case MAC_VIA_II:
32962306a36Sopenharmony_ci	case MAC_VIA_QUADRA:
33062306a36Sopenharmony_ci		pr_debug("VIA2 vDirA is 0x%02X\n", via2[vDirA]);
33162306a36Sopenharmony_ci		break;
33262306a36Sopenharmony_ci	case MAC_VIA_IICI:
33362306a36Sopenharmony_ci		/* RBV. Disable all the slot interrupts. SIER works like IER. */
33462306a36Sopenharmony_ci		via2[rSIER] = 0x7F;
33562306a36Sopenharmony_ci		break;
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_civoid via_nubus_irq_startup(int irq)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	int irq_idx = IRQ_IDX(irq);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	switch (macintosh_config->via_type) {
34462306a36Sopenharmony_ci	case MAC_VIA_II:
34562306a36Sopenharmony_ci	case MAC_VIA_QUADRA:
34662306a36Sopenharmony_ci		/* Make the port A line an input. Probably redundant. */
34762306a36Sopenharmony_ci		if (macintosh_config->via_type == MAC_VIA_II) {
34862306a36Sopenharmony_ci			/* The top two bits are RAM size outputs. */
34962306a36Sopenharmony_ci			via2[vDirA] &= 0xC0 | ~(1 << irq_idx);
35062306a36Sopenharmony_ci		} else {
35162306a36Sopenharmony_ci			/* Allow NuBus slots 9 through F. */
35262306a36Sopenharmony_ci			via2[vDirA] &= 0x80 | ~(1 << irq_idx);
35362306a36Sopenharmony_ci		}
35462306a36Sopenharmony_ci		fallthrough;
35562306a36Sopenharmony_ci	case MAC_VIA_IICI:
35662306a36Sopenharmony_ci		via_irq_enable(irq);
35762306a36Sopenharmony_ci		break;
35862306a36Sopenharmony_ci	}
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_civoid via_nubus_irq_shutdown(int irq)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	switch (macintosh_config->via_type) {
36462306a36Sopenharmony_ci	case MAC_VIA_II:
36562306a36Sopenharmony_ci	case MAC_VIA_QUADRA:
36662306a36Sopenharmony_ci		/* Ensure that the umbrella CA1 interrupt remains enabled. */
36762306a36Sopenharmony_ci		via_irq_enable(irq);
36862306a36Sopenharmony_ci		break;
36962306a36Sopenharmony_ci	case MAC_VIA_IICI:
37062306a36Sopenharmony_ci		via_irq_disable(irq);
37162306a36Sopenharmony_ci		break;
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci/*
37662306a36Sopenharmony_ci * The generic VIA interrupt routines (shamelessly stolen from Alan Cox's
37762306a36Sopenharmony_ci * via6522.c :-), disable/pending masks added.
37862306a36Sopenharmony_ci */
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci#define VIA_TIMER_1_INT BIT(6)
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_civoid via1_irq(struct irq_desc *desc)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	int irq_num;
38562306a36Sopenharmony_ci	unsigned char irq_bit, events;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	events = via1[vIFR] & via1[vIER] & 0x7F;
38862306a36Sopenharmony_ci	if (!events)
38962306a36Sopenharmony_ci		return;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	irq_num = IRQ_MAC_TIMER_1;
39262306a36Sopenharmony_ci	irq_bit = VIA_TIMER_1_INT;
39362306a36Sopenharmony_ci	if (events & irq_bit) {
39462306a36Sopenharmony_ci		unsigned long flags;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci		local_irq_save(flags);
39762306a36Sopenharmony_ci		via1[vIFR] = irq_bit;
39862306a36Sopenharmony_ci		generic_handle_irq(irq_num);
39962306a36Sopenharmony_ci		local_irq_restore(flags);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci		events &= ~irq_bit;
40262306a36Sopenharmony_ci		if (!events)
40362306a36Sopenharmony_ci			return;
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	irq_num = VIA1_SOURCE_BASE;
40762306a36Sopenharmony_ci	irq_bit = 1;
40862306a36Sopenharmony_ci	do {
40962306a36Sopenharmony_ci		if (events & irq_bit) {
41062306a36Sopenharmony_ci			via1[vIFR] = irq_bit;
41162306a36Sopenharmony_ci			generic_handle_irq(irq_num);
41262306a36Sopenharmony_ci		}
41362306a36Sopenharmony_ci		++irq_num;
41462306a36Sopenharmony_ci		irq_bit <<= 1;
41562306a36Sopenharmony_ci	} while (events >= irq_bit);
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistatic void via2_irq(struct irq_desc *desc)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	int irq_num;
42162306a36Sopenharmony_ci	unsigned char irq_bit, events;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	events = via2[gIFR] & via2[gIER] & 0x7F;
42462306a36Sopenharmony_ci	if (!events)
42562306a36Sopenharmony_ci		return;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	irq_num = VIA2_SOURCE_BASE;
42862306a36Sopenharmony_ci	irq_bit = 1;
42962306a36Sopenharmony_ci	do {
43062306a36Sopenharmony_ci		if (events & irq_bit) {
43162306a36Sopenharmony_ci			via2[gIFR] = irq_bit | rbv_clear;
43262306a36Sopenharmony_ci			generic_handle_irq(irq_num);
43362306a36Sopenharmony_ci		}
43462306a36Sopenharmony_ci		++irq_num;
43562306a36Sopenharmony_ci		irq_bit <<= 1;
43662306a36Sopenharmony_ci	} while (events >= irq_bit);
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci/*
44062306a36Sopenharmony_ci * Dispatch Nubus interrupts. We are called as a secondary dispatch by the
44162306a36Sopenharmony_ci * VIA2 dispatcher as a fast interrupt handler.
44262306a36Sopenharmony_ci */
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic void via_nubus_irq(struct irq_desc *desc)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	int slot_irq;
44762306a36Sopenharmony_ci	unsigned char slot_bit, events;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	events = ~via2[gBufA] & 0x7F;
45062306a36Sopenharmony_ci	if (rbv_present)
45162306a36Sopenharmony_ci		events &= via2[rSIER];
45262306a36Sopenharmony_ci	else
45362306a36Sopenharmony_ci		events &= ~via2[vDirA];
45462306a36Sopenharmony_ci	if (!events)
45562306a36Sopenharmony_ci		return;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	do {
45862306a36Sopenharmony_ci		slot_irq = IRQ_NUBUS_F;
45962306a36Sopenharmony_ci		slot_bit = 0x40;
46062306a36Sopenharmony_ci		do {
46162306a36Sopenharmony_ci			if (events & slot_bit) {
46262306a36Sopenharmony_ci				events &= ~slot_bit;
46362306a36Sopenharmony_ci				generic_handle_irq(slot_irq);
46462306a36Sopenharmony_ci			}
46562306a36Sopenharmony_ci			--slot_irq;
46662306a36Sopenharmony_ci			slot_bit >>= 1;
46762306a36Sopenharmony_ci		} while (events);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci 		/* clear the CA1 interrupt and make certain there's no more. */
47062306a36Sopenharmony_ci		via2[gIFR] = 0x02 | rbv_clear;
47162306a36Sopenharmony_ci		events = ~via2[gBufA] & 0x7F;
47262306a36Sopenharmony_ci		if (rbv_present)
47362306a36Sopenharmony_ci			events &= via2[rSIER];
47462306a36Sopenharmony_ci		else
47562306a36Sopenharmony_ci			events &= ~via2[vDirA];
47662306a36Sopenharmony_ci	} while (events);
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci/*
48062306a36Sopenharmony_ci * Register the interrupt dispatchers for VIA or RBV machines only.
48162306a36Sopenharmony_ci */
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_civoid __init via_register_interrupts(void)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	if (via_alt_mapping) {
48662306a36Sopenharmony_ci		/* software interrupt */
48762306a36Sopenharmony_ci		irq_set_chained_handler(IRQ_AUTO_1, via1_irq);
48862306a36Sopenharmony_ci		/* via1 interrupt */
48962306a36Sopenharmony_ci		irq_set_chained_handler(IRQ_AUTO_6, via1_irq);
49062306a36Sopenharmony_ci	} else {
49162306a36Sopenharmony_ci		irq_set_chained_handler(IRQ_AUTO_1, via1_irq);
49262306a36Sopenharmony_ci	}
49362306a36Sopenharmony_ci	irq_set_chained_handler(IRQ_AUTO_2, via2_irq);
49462306a36Sopenharmony_ci	irq_set_chained_handler(IRQ_MAC_NUBUS, via_nubus_irq);
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_civoid via_irq_enable(int irq) {
49862306a36Sopenharmony_ci	int irq_src	= IRQ_SRC(irq);
49962306a36Sopenharmony_ci	int irq_idx	= IRQ_IDX(irq);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	if (irq_src == 1) {
50262306a36Sopenharmony_ci		via1[vIER] = IER_SET_BIT(irq_idx);
50362306a36Sopenharmony_ci	} else if (irq_src == 2) {
50462306a36Sopenharmony_ci		if (irq != IRQ_MAC_NUBUS || nubus_disabled == 0)
50562306a36Sopenharmony_ci			via2[gIER] = IER_SET_BIT(irq_idx);
50662306a36Sopenharmony_ci	} else if (irq_src == 7) {
50762306a36Sopenharmony_ci		switch (macintosh_config->via_type) {
50862306a36Sopenharmony_ci		case MAC_VIA_II:
50962306a36Sopenharmony_ci		case MAC_VIA_QUADRA:
51062306a36Sopenharmony_ci			nubus_disabled &= ~(1 << irq_idx);
51162306a36Sopenharmony_ci			/* Enable the CA1 interrupt when no slot is disabled. */
51262306a36Sopenharmony_ci			if (!nubus_disabled)
51362306a36Sopenharmony_ci				via2[gIER] = IER_SET_BIT(1);
51462306a36Sopenharmony_ci			break;
51562306a36Sopenharmony_ci		case MAC_VIA_IICI:
51662306a36Sopenharmony_ci			/* On RBV, enable the slot interrupt.
51762306a36Sopenharmony_ci			 * SIER works like IER.
51862306a36Sopenharmony_ci			 */
51962306a36Sopenharmony_ci			via2[rSIER] = IER_SET_BIT(irq_idx);
52062306a36Sopenharmony_ci			break;
52162306a36Sopenharmony_ci		}
52262306a36Sopenharmony_ci	}
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_civoid via_irq_disable(int irq) {
52662306a36Sopenharmony_ci	int irq_src	= IRQ_SRC(irq);
52762306a36Sopenharmony_ci	int irq_idx	= IRQ_IDX(irq);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	if (irq_src == 1) {
53062306a36Sopenharmony_ci		via1[vIER] = IER_CLR_BIT(irq_idx);
53162306a36Sopenharmony_ci	} else if (irq_src == 2) {
53262306a36Sopenharmony_ci		via2[gIER] = IER_CLR_BIT(irq_idx);
53362306a36Sopenharmony_ci	} else if (irq_src == 7) {
53462306a36Sopenharmony_ci		switch (macintosh_config->via_type) {
53562306a36Sopenharmony_ci		case MAC_VIA_II:
53662306a36Sopenharmony_ci		case MAC_VIA_QUADRA:
53762306a36Sopenharmony_ci			nubus_disabled |= 1 << irq_idx;
53862306a36Sopenharmony_ci			if (nubus_disabled)
53962306a36Sopenharmony_ci				via2[gIER] = IER_CLR_BIT(1);
54062306a36Sopenharmony_ci			break;
54162306a36Sopenharmony_ci		case MAC_VIA_IICI:
54262306a36Sopenharmony_ci			via2[rSIER] = IER_CLR_BIT(irq_idx);
54362306a36Sopenharmony_ci			break;
54462306a36Sopenharmony_ci		}
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ci}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_civoid via1_set_head(int head)
54962306a36Sopenharmony_ci{
55062306a36Sopenharmony_ci	if (head == 0)
55162306a36Sopenharmony_ci		via1[vBufA] &= ~VIA1A_vHeadSel;
55262306a36Sopenharmony_ci	else
55362306a36Sopenharmony_ci		via1[vBufA] |= VIA1A_vHeadSel;
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ciEXPORT_SYMBOL(via1_set_head);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ciint via2_scsi_drq_pending(void)
55862306a36Sopenharmony_ci{
55962306a36Sopenharmony_ci	return via2[gIFR] & (1 << IRQ_IDX(IRQ_MAC_SCSIDRQ));
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ciEXPORT_SYMBOL(via2_scsi_drq_pending);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci/* timer and clock source */
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci#define VIA_CLOCK_FREQ     783360                /* VIA "phase 2" clock in Hz */
56662306a36Sopenharmony_ci#define VIA_TIMER_CYCLES   (VIA_CLOCK_FREQ / HZ) /* clock cycles per jiffy */
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci#define VIA_TC             (VIA_TIMER_CYCLES - 2) /* including 0 and -1 */
56962306a36Sopenharmony_ci#define VIA_TC_LOW         (VIA_TC & 0xFF)
57062306a36Sopenharmony_ci#define VIA_TC_HIGH        (VIA_TC >> 8)
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cistatic u64 mac_read_clk(struct clocksource *cs);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_cistatic struct clocksource mac_clk = {
57562306a36Sopenharmony_ci	.name   = "via1",
57662306a36Sopenharmony_ci	.rating = 250,
57762306a36Sopenharmony_ci	.read   = mac_read_clk,
57862306a36Sopenharmony_ci	.mask   = CLOCKSOURCE_MASK(32),
57962306a36Sopenharmony_ci	.flags  = CLOCK_SOURCE_IS_CONTINUOUS,
58062306a36Sopenharmony_ci};
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic u32 clk_total, clk_offset;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cistatic irqreturn_t via_timer_handler(int irq, void *dev_id)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	clk_total += VIA_TIMER_CYCLES;
58762306a36Sopenharmony_ci	clk_offset = 0;
58862306a36Sopenharmony_ci	legacy_timer_tick(1);
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	return IRQ_HANDLED;
59162306a36Sopenharmony_ci}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_civoid __init via_init_clock(void)
59462306a36Sopenharmony_ci{
59562306a36Sopenharmony_ci	if (request_irq(IRQ_MAC_TIMER_1, via_timer_handler, IRQF_TIMER, "timer",
59662306a36Sopenharmony_ci			NULL)) {
59762306a36Sopenharmony_ci		pr_err("Couldn't register %s interrupt\n", "timer");
59862306a36Sopenharmony_ci		return;
59962306a36Sopenharmony_ci	}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	via1[vT1CL] = VIA_TC_LOW;
60262306a36Sopenharmony_ci	via1[vT1CH] = VIA_TC_HIGH;
60362306a36Sopenharmony_ci	via1[vACR] |= 0x40;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	clocksource_register_hz(&mac_clk, VIA_CLOCK_FREQ);
60662306a36Sopenharmony_ci}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_cistatic u64 mac_read_clk(struct clocksource *cs)
60962306a36Sopenharmony_ci{
61062306a36Sopenharmony_ci	unsigned long flags;
61162306a36Sopenharmony_ci	u8 count_high;
61262306a36Sopenharmony_ci	u16 count;
61362306a36Sopenharmony_ci	u32 ticks;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	/*
61662306a36Sopenharmony_ci	 * Timer counter wrap-around is detected with the timer interrupt flag
61762306a36Sopenharmony_ci	 * but reading the counter low byte (vT1CL) would reset the flag.
61862306a36Sopenharmony_ci	 * Also, accessing both counter registers is essentially a data race.
61962306a36Sopenharmony_ci	 * These problems are avoided by ignoring the low byte. Clock accuracy
62062306a36Sopenharmony_ci	 * is 256 times worse (error can reach 0.327 ms) but CPU overhead is
62162306a36Sopenharmony_ci	 * reduced by avoiding slow VIA register accesses.
62262306a36Sopenharmony_ci	 */
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	local_irq_save(flags);
62562306a36Sopenharmony_ci	count_high = via1[vT1CH];
62662306a36Sopenharmony_ci	if (count_high == 0xFF)
62762306a36Sopenharmony_ci		count_high = 0;
62862306a36Sopenharmony_ci	if (count_high > 0 && (via1[vIFR] & VIA_TIMER_1_INT))
62962306a36Sopenharmony_ci		clk_offset = VIA_TIMER_CYCLES;
63062306a36Sopenharmony_ci	count = count_high << 8;
63162306a36Sopenharmony_ci	ticks = VIA_TIMER_CYCLES - count;
63262306a36Sopenharmony_ci	ticks += clk_offset + clk_total;
63362306a36Sopenharmony_ci	local_irq_restore(flags);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	return ticks;
63662306a36Sopenharmony_ci}
637