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