// SPDX-License-Identifier: GPL-2.01/*2* 6522 Versatile Interface Adapter (VIA)3*4* There are two of these on the Mac II. Some IRQs are vectored5* via them as are assorted bits and bobs - eg RTC, ADB.6*7* CSA: Motorola seems to have removed documentation on the 6522 from8* their web site; try9* http://nerini.drf.com/vectrex/other/text/chips/6522/10* http://www.zymurgy.net/classic/vic20/vicdet1.htm11* and12* http://193.23.168.87/mikro_laborversuche/via_iobaustein/via6522_1.html13* for info. A full-text web search on 6522 AND VIA will probably also14* net some usefulness. <[email protected]> 20apr199915*16* Additional data is here (the SY6522 was used in the Mac II etc):17* http://www.6502.org/documents/datasheets/synertek/synertek_sy6522.pdf18* http://www.6502.org/documents/datasheets/synertek/synertek_sy6522_programming_reference.pdf19*20* PRAM/RTC access algorithms are from the NetBSD RTC toolkit version 1.08b21* by Erik Vogan and adapted to Linux by Joshua M. Thompson ([email protected])22*23*/2425#include <linux/clocksource.h>26#include <linux/types.h>27#include <linux/kernel.h>28#include <linux/mm.h>29#include <linux/delay.h>30#include <linux/init.h>31#include <linux/module.h>32#include <linux/irq.h>3334#include <asm/macintosh.h>35#include <asm/macints.h>36#include <asm/mac_via.h>37#include <asm/mac_psc.h>38#include <asm/mac_oss.h>3940#include "mac.h"4142volatile __u8 *via1, *via2;43int rbv_present;44int via_alt_mapping;45EXPORT_SYMBOL(via_alt_mapping);46static __u8 rbv_clear;4748/*49* Globals for accessing the VIA chip registers without having to50* check if we're hitting a real VIA or an RBV. Normally you could51* just hit the combined register (ie, vIER|rIER) but that seems to52* break on AV Macs...probably because they actually decode more than53* eight address bits. Why can't Apple engineers at least be54* _consistently_ lazy? - 1999-05-21 (jmt)55*/5657static int gIER,gIFR,gBufA,gBufB;5859/*60* On Macs with a genuine VIA chip there is no way to mask an individual slot61* interrupt. This limitation also seems to apply to VIA clone logic cores in62* Quadra-like ASICs. (RBV and OSS machines don't have this limitation.)63*64* We used to fake it by configuring the relevant VIA pin as an output65* (to mask the interrupt) or input (to unmask). That scheme did not work on66* (at least) the Quadra 700. A NuBus card's /NMRQ signal is an open-collector67* circuit (see Designing Cards and Drivers for Macintosh II and Macintosh SE,68* p. 10-11 etc) but VIA outputs are not (see datasheet).69*70* Driving these outputs high must cause the VIA to source current and the71* card to sink current when it asserts /NMRQ. Current will flow but the pin72* voltage is uncertain and so the /NMRQ condition may still cause a transition73* at the VIA2 CA1 input (which explains the lost interrupts). A side effect74* is that a disabled slot IRQ can never be tested as pending or not.75*76* Driving these outputs low doesn't work either. All the slot /NMRQ lines are77* (active low) OR'd together to generate the CA1 (aka "SLOTS") interrupt (see78* The Guide To Macintosh Family Hardware, 2nd edition p. 167). If we drive a79* disabled /NMRQ line low, the falling edge immediately triggers a CA180* interrupt and all slot interrupts after that will generate no transition81* and therefore no interrupt, even after being re-enabled.82*83* So we make the VIA port A I/O lines inputs and use nubus_disabled to keep84* track of their states. When any slot IRQ becomes disabled we mask the CA185* umbrella interrupt. Only when all slot IRQs become enabled do we unmask86* the CA1 interrupt. It must remain enabled even when cards have no interrupt87* handler registered. Drivers must therefore disable a slot interrupt at the88* device before they call free_irq (like shared and autovector interrupts).89*90* There is also a related problem when MacOS is used to boot Linux. A network91* card brought up by a MacOS driver may raise an interrupt while Linux boots.92* This can be fatal since it can't be handled until the right driver loads93* (if such a driver exists at all). Apparently related to this hardware94* limitation, "Designing Cards and Drivers", p. 9-8, says that a slot95* interrupt with no driver would crash MacOS (the book was written before96* the appearance of Macs with RBV or OSS).97*/9899static u8 nubus_disabled;100101void via_debug_dump(void);102static void via_nubus_init(void);103104/*105* Initialize the VIAs106*107* First we figure out where they actually _are_ as well as what type of108* VIA we have for VIA2 (it could be a real VIA or an RBV or even an OSS.)109* Then we pretty much clear them out and disable all IRQ sources.110*/111112void __init via_init(void)113{114via1 = (void *)VIA1_BASE;115pr_debug("VIA1 detected at %p\n", via1);116117if (oss_present) {118via2 = NULL;119rbv_present = 0;120} else {121switch (macintosh_config->via_type) {122123/* IIci, IIsi, IIvx, IIvi (P6xx), LC series */124125case MAC_VIA_IICI:126via2 = (void *)RBV_BASE;127pr_debug("VIA2 (RBV) detected at %p\n", via2);128rbv_present = 1;129if (macintosh_config->ident == MAC_MODEL_LCIII) {130rbv_clear = 0x00;131} else {132/* on most RBVs (& unlike the VIAs), you */133/* need to set bit 7 when you write to IFR */134/* in order for your clear to occur. */135rbv_clear = 0x80;136}137gIER = rIER;138gIFR = rIFR;139gBufA = rSIFR;140gBufB = rBufB;141break;142143/* Quadra and early MacIIs agree on the VIA locations */144145case MAC_VIA_QUADRA:146case MAC_VIA_II:147via2 = (void *) VIA2_BASE;148pr_debug("VIA2 detected at %p\n", via2);149rbv_present = 0;150rbv_clear = 0x00;151gIER = vIER;152gIFR = vIFR;153gBufA = vBufA;154gBufB = vBufB;155break;156157default:158panic("UNKNOWN VIA TYPE");159}160}161162#ifdef DEBUG_VIA163via_debug_dump();164#endif165166/*167* Shut down all IRQ sources, reset the timers, and168* kill the timer latch on VIA1.169*/170171via1[vIER] = 0x7F;172via1[vIFR] = 0x7F;173via1[vT1CL] = 0;174via1[vT1CH] = 0;175via1[vT2CL] = 0;176via1[vT2CH] = 0;177via1[vACR] &= ~0xC0; /* setup T1 timer with no PB7 output */178via1[vACR] &= ~0x03; /* disable port A & B latches */179180/*181* SE/30: disable video IRQ182*/183184if (macintosh_config->ident == MAC_MODEL_SE30) {185via1[vDirB] |= 0x40;186via1[vBufB] |= 0x40;187}188189switch (macintosh_config->adb_type) {190case MAC_ADB_IOP:191case MAC_ADB_II:192case MAC_ADB_PB1:193/*194* Set the RTC bits to a known state: all lines to outputs and195* RTC disabled (yes that's 0 to enable and 1 to disable).196*/197via1[vDirB] |= VIA1B_vRTCEnb | VIA1B_vRTCClk | VIA1B_vRTCData;198via1[vBufB] |= VIA1B_vRTCEnb | VIA1B_vRTCClk;199break;200}201202/* Everything below this point is VIA2/RBV only... */203204if (oss_present)205return;206207if ((macintosh_config->via_type == MAC_VIA_QUADRA) &&208(macintosh_config->adb_type != MAC_ADB_PB1) &&209(macintosh_config->adb_type != MAC_ADB_PB2) &&210(macintosh_config->ident != MAC_MODEL_C660) &&211(macintosh_config->ident != MAC_MODEL_Q840)) {212via_alt_mapping = 1;213via1[vDirB] |= 0x40;214via1[vBufB] &= ~0x40;215} else {216via_alt_mapping = 0;217}218219/*220* Now initialize VIA2. For RBV we just kill all interrupts;221* for a regular VIA we also reset the timers and stuff.222*/223224via2[gIER] = 0x7F;225via2[gIFR] = 0x7F | rbv_clear;226if (!rbv_present) {227via2[vT1CL] = 0;228via2[vT1CH] = 0;229via2[vT2CL] = 0;230via2[vT2CH] = 0;231via2[vACR] &= ~0xC0; /* setup T1 timer with no PB7 output */232via2[vACR] &= ~0x03; /* disable port A & B latches */233}234235via_nubus_init();236237/* Everything below this point is VIA2 only... */238239if (rbv_present)240return;241242/*243* Set vPCR for control line interrupts.244*245* CA1 (SLOTS IRQ), CB1 (ASC IRQ): negative edge trigger.246*247* Macs with ESP SCSI have a negative edge triggered SCSI interrupt.248* Testing reveals that PowerBooks do too. However, the SE/30249* schematic diagram shows an active high NCR5380 IRQ line.250*/251252pr_debug("VIA2 vPCR is 0x%02X\n", via2[vPCR]);253if (macintosh_config->via_type == MAC_VIA_II) {254/* CA2 (SCSI DRQ), CB2 (SCSI IRQ): indep. input, pos. edge */255via2[vPCR] = 0x66;256} else {257/* CA2 (SCSI DRQ), CB2 (SCSI IRQ): indep. input, neg. edge */258via2[vPCR] = 0x22;259}260}261262/*263* Debugging dump, used in various places to see what's going on.264*/265266void via_debug_dump(void)267{268printk(KERN_DEBUG "VIA1: DDRA = 0x%02X DDRB = 0x%02X ACR = 0x%02X\n",269(uint) via1[vDirA], (uint) via1[vDirB], (uint) via1[vACR]);270printk(KERN_DEBUG " PCR = 0x%02X IFR = 0x%02X IER = 0x%02X\n",271(uint) via1[vPCR], (uint) via1[vIFR], (uint) via1[vIER]);272if (!via2)273return;274if (rbv_present) {275printk(KERN_DEBUG "VIA2: IFR = 0x%02X IER = 0x%02X\n",276(uint) via2[rIFR], (uint) via2[rIER]);277printk(KERN_DEBUG " SIFR = 0x%02X SIER = 0x%02X\n",278(uint) via2[rSIFR], (uint) via2[rSIER]);279} else {280printk(KERN_DEBUG "VIA2: DDRA = 0x%02X DDRB = 0x%02X ACR = 0x%02X\n",281(uint) via2[vDirA], (uint) via2[vDirB],282(uint) via2[vACR]);283printk(KERN_DEBUG " PCR = 0x%02X IFR = 0x%02X IER = 0x%02X\n",284(uint) via2[vPCR],285(uint) via2[vIFR], (uint) via2[vIER]);286}287}288289/*290* Flush the L2 cache on Macs that have it by flipping291* the system into 24-bit mode for an instant.292*/293294void via_l2_flush(int writeback)295{296unsigned long flags;297298local_irq_save(flags);299via2[gBufB] &= ~VIA2B_vMode32;300via2[gBufB] |= VIA2B_vMode32;301local_irq_restore(flags);302}303304/*305* Initialize VIA2 for Nubus access306*/307308static void __init via_nubus_init(void)309{310/* unlock nubus transactions */311312if ((macintosh_config->adb_type != MAC_ADB_PB1) &&313(macintosh_config->adb_type != MAC_ADB_PB2)) {314/* set the line to be an output on non-RBV machines */315if (!rbv_present)316via2[vDirB] |= 0x02;317318/* this seems to be an ADB bit on PMU machines */319/* according to MkLinux. -- jmt */320via2[gBufB] |= 0x02;321}322323/*324* Disable the slot interrupts. On some hardware that's not possible.325* On some hardware it's unclear what all of these I/O lines do.326*/327328switch (macintosh_config->via_type) {329case MAC_VIA_II:330case MAC_VIA_QUADRA:331pr_debug("VIA2 vDirA is 0x%02X\n", via2[vDirA]);332break;333case MAC_VIA_IICI:334/* RBV. Disable all the slot interrupts. SIER works like IER. */335via2[rSIER] = 0x7F;336break;337}338}339340void via_nubus_irq_startup(int irq)341{342int irq_idx = IRQ_IDX(irq);343344switch (macintosh_config->via_type) {345case MAC_VIA_II:346case MAC_VIA_QUADRA:347/* Make the port A line an input. Probably redundant. */348if (macintosh_config->via_type == MAC_VIA_II) {349/* The top two bits are RAM size outputs. */350via2[vDirA] &= 0xC0 | ~(1 << irq_idx);351} else {352/* Allow NuBus slots 9 through F. */353via2[vDirA] &= 0x80 | ~(1 << irq_idx);354}355fallthrough;356case MAC_VIA_IICI:357via_irq_enable(irq);358break;359}360}361362void via_nubus_irq_shutdown(int irq)363{364switch (macintosh_config->via_type) {365case MAC_VIA_II:366case MAC_VIA_QUADRA:367/* Ensure that the umbrella CA1 interrupt remains enabled. */368via_irq_enable(irq);369break;370case MAC_VIA_IICI:371via_irq_disable(irq);372break;373}374}375376/*377* The generic VIA interrupt routines (shamelessly stolen from Alan Cox's378* via6522.c :-), disable/pending masks added.379*/380381#define VIA_TIMER_1_INT BIT(6)382383void via1_irq(struct irq_desc *desc)384{385int irq_num;386unsigned char irq_bit, events;387388events = via1[vIFR] & via1[vIER] & 0x7F;389if (!events)390return;391392irq_num = IRQ_MAC_TIMER_1;393irq_bit = VIA_TIMER_1_INT;394if (events & irq_bit) {395unsigned long flags;396397local_irq_save(flags);398via1[vIFR] = irq_bit;399generic_handle_irq(irq_num);400local_irq_restore(flags);401402events &= ~irq_bit;403if (!events)404return;405}406407irq_num = VIA1_SOURCE_BASE;408irq_bit = 1;409do {410if (events & irq_bit) {411via1[vIFR] = irq_bit;412generic_handle_irq(irq_num);413}414++irq_num;415irq_bit <<= 1;416} while (events >= irq_bit);417}418419static void via2_irq(struct irq_desc *desc)420{421int irq_num;422unsigned char irq_bit, events;423424events = via2[gIFR] & via2[gIER] & 0x7F;425if (!events)426return;427428irq_num = VIA2_SOURCE_BASE;429irq_bit = 1;430do {431if (events & irq_bit) {432via2[gIFR] = irq_bit | rbv_clear;433generic_handle_irq(irq_num);434}435++irq_num;436irq_bit <<= 1;437} while (events >= irq_bit);438}439440/*441* Dispatch Nubus interrupts. We are called as a secondary dispatch by the442* VIA2 dispatcher as a fast interrupt handler.443*/444445static void via_nubus_irq(struct irq_desc *desc)446{447int slot_irq;448unsigned char slot_bit, events;449450events = ~via2[gBufA] & 0x7F;451if (rbv_present)452events &= via2[rSIER];453else454events &= ~via2[vDirA];455if (!events)456return;457458do {459slot_irq = IRQ_NUBUS_F;460slot_bit = 0x40;461do {462if (events & slot_bit) {463events &= ~slot_bit;464generic_handle_irq(slot_irq);465}466--slot_irq;467slot_bit >>= 1;468} while (events);469470/* clear the CA1 interrupt and make certain there's no more. */471via2[gIFR] = 0x02 | rbv_clear;472events = ~via2[gBufA] & 0x7F;473if (rbv_present)474events &= via2[rSIER];475else476events &= ~via2[vDirA];477} while (events);478}479480/*481* Register the interrupt dispatchers for VIA or RBV machines only.482*/483484void __init via_register_interrupts(void)485{486if (via_alt_mapping) {487/* software interrupt */488irq_set_chained_handler(IRQ_AUTO_1, via1_irq);489/* via1 interrupt */490irq_set_chained_handler(IRQ_AUTO_6, via1_irq);491} else {492irq_set_chained_handler(IRQ_AUTO_1, via1_irq);493}494irq_set_chained_handler(IRQ_AUTO_2, via2_irq);495irq_set_chained_handler(IRQ_MAC_NUBUS, via_nubus_irq);496}497498void via_irq_enable(int irq) {499int irq_src = IRQ_SRC(irq);500int irq_idx = IRQ_IDX(irq);501502if (irq_src == 1) {503via1[vIER] = IER_SET_BIT(irq_idx);504} else if (irq_src == 2) {505if (irq != IRQ_MAC_NUBUS || nubus_disabled == 0)506via2[gIER] = IER_SET_BIT(irq_idx);507} else if (irq_src == 7) {508switch (macintosh_config->via_type) {509case MAC_VIA_II:510case MAC_VIA_QUADRA:511nubus_disabled &= ~(1 << irq_idx);512/* Enable the CA1 interrupt when no slot is disabled. */513if (!nubus_disabled)514via2[gIER] = IER_SET_BIT(1);515break;516case MAC_VIA_IICI:517/* On RBV, enable the slot interrupt.518* SIER works like IER.519*/520via2[rSIER] = IER_SET_BIT(irq_idx);521break;522}523}524}525526void via_irq_disable(int irq) {527int irq_src = IRQ_SRC(irq);528int irq_idx = IRQ_IDX(irq);529530if (irq_src == 1) {531via1[vIER] = IER_CLR_BIT(irq_idx);532} else if (irq_src == 2) {533via2[gIER] = IER_CLR_BIT(irq_idx);534} else if (irq_src == 7) {535switch (macintosh_config->via_type) {536case MAC_VIA_II:537case MAC_VIA_QUADRA:538nubus_disabled |= 1 << irq_idx;539if (nubus_disabled)540via2[gIER] = IER_CLR_BIT(1);541break;542case MAC_VIA_IICI:543via2[rSIER] = IER_CLR_BIT(irq_idx);544break;545}546}547}548549void via1_set_head(int head)550{551if (head == 0)552via1[vBufA] &= ~VIA1A_vHeadSel;553else554via1[vBufA] |= VIA1A_vHeadSel;555}556EXPORT_SYMBOL(via1_set_head);557558int via2_scsi_drq_pending(void)559{560return via2[gIFR] & (1 << IRQ_IDX(IRQ_MAC_SCSIDRQ));561}562EXPORT_SYMBOL(via2_scsi_drq_pending);563564/* timer and clock source */565566#define VIA_CLOCK_FREQ 783360 /* VIA "phase 2" clock in Hz */567#define VIA_TIMER_CYCLES (VIA_CLOCK_FREQ / HZ) /* clock cycles per jiffy */568569#define VIA_TC (VIA_TIMER_CYCLES - 2) /* including 0 and -1 */570#define VIA_TC_LOW (VIA_TC & 0xFF)571#define VIA_TC_HIGH (VIA_TC >> 8)572573static u64 mac_read_clk(struct clocksource *cs);574575static struct clocksource mac_clk = {576.name = "via1",577.rating = 250,578.read = mac_read_clk,579.mask = CLOCKSOURCE_MASK(32),580.flags = CLOCK_SOURCE_IS_CONTINUOUS,581};582583static u32 clk_total, clk_offset;584585static irqreturn_t via_timer_handler(int irq, void *dev_id)586{587clk_total += VIA_TIMER_CYCLES;588clk_offset = 0;589legacy_timer_tick(1);590591return IRQ_HANDLED;592}593594void __init via_init_clock(void)595{596if (request_irq(IRQ_MAC_TIMER_1, via_timer_handler, IRQF_TIMER, "timer",597NULL)) {598pr_err("Couldn't register %s interrupt\n", "timer");599return;600}601602via1[vT1CL] = VIA_TC_LOW;603via1[vT1CH] = VIA_TC_HIGH;604via1[vACR] |= 0x40;605606clocksource_register_hz(&mac_clk, VIA_CLOCK_FREQ);607}608609static u64 mac_read_clk(struct clocksource *cs)610{611unsigned long flags;612u8 count_high;613u16 count;614u32 ticks;615616/*617* Timer counter wrap-around is detected with the timer interrupt flag618* but reading the counter low byte (vT1CL) would reset the flag.619* Also, accessing both counter registers is essentially a data race.620* These problems are avoided by ignoring the low byte. Clock accuracy621* is 256 times worse (error can reach 0.327 ms) but CPU overhead is622* reduced by avoiding slow VIA register accesses.623*624* The VIA timer counter observably decrements to 0xFFFF before the625* counter reload interrupt gets raised. That complicates things a bit.626*627* State | vT1CH | VIA_TIMER_1_INT | inference drawn628* ------+------------+-----------------+-----------------------------629* i | FE thru 00 | false | counter is decrementing630* ii | FF | false | counter wrapped631* iii | FF | true | wrapped, interrupt raised632* iv | FF | false | wrapped, interrupt handled633* v | FE thru 00 | true | wrapped, interrupt unhandled634*635* State iv is never observed because handling the interrupt involves636* a 6522 register access and every access consumes a "phi 2" clock637* cycle. So 0xFF implies either state ii or state iii, depending on638* the value of the VIA_TIMER_1_INT bit.639*/640641local_irq_save(flags);642count_high = via1[vT1CH];643if (count_high == 0xFF)644count_high = 0;645if (count_high > 0 && (via1[vIFR] & VIA_TIMER_1_INT))646clk_offset = VIA_TIMER_CYCLES;647count = count_high << 8;648ticks = VIA_TIMER_CYCLES - count;649ticks += clk_offset + clk_total;650local_irq_restore(flags);651652return ticks;653}654655656