18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Operating System Services (OSS) chip handling 48c2ecf20Sopenharmony_ci * Written by Joshua M. Thompson (funaho@jurai.org) 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This chip is used in the IIfx in place of VIA #2. It acts like a fancy 88c2ecf20Sopenharmony_ci * VIA chip with prorammable interrupt levels. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * 990502 (jmt) - Major rewrite for new interrupt architecture as well as some 118c2ecf20Sopenharmony_ci * recent insights into OSS operational details. 128c2ecf20Sopenharmony_ci * 990610 (jmt) - Now taking full advantage of the OSS. Interrupts are mapped 138c2ecf20Sopenharmony_ci * to mostly match the A/UX interrupt scheme supported on the 148c2ecf20Sopenharmony_ci * VIA side. Also added support for enabling the ISM irq again 158c2ecf20Sopenharmony_ci * since we now have a functional IOP manager. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/types.h> 198c2ecf20Sopenharmony_ci#include <linux/kernel.h> 208c2ecf20Sopenharmony_ci#include <linux/mm.h> 218c2ecf20Sopenharmony_ci#include <linux/delay.h> 228c2ecf20Sopenharmony_ci#include <linux/init.h> 238c2ecf20Sopenharmony_ci#include <linux/irq.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <asm/macintosh.h> 268c2ecf20Sopenharmony_ci#include <asm/macints.h> 278c2ecf20Sopenharmony_ci#include <asm/mac_via.h> 288c2ecf20Sopenharmony_ci#include <asm/mac_oss.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ciint oss_present; 318c2ecf20Sopenharmony_civolatile struct mac_oss *oss; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* 348c2ecf20Sopenharmony_ci * Initialize the OSS 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_civoid __init oss_init(void) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci int i; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci if (macintosh_config->ident != MAC_MODEL_IIFX) 428c2ecf20Sopenharmony_ci return; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci oss = (struct mac_oss *) OSS_BASE; 458c2ecf20Sopenharmony_ci pr_debug("OSS detected at %p", oss); 468c2ecf20Sopenharmony_ci oss_present = 1; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci /* Disable all interrupts. Unlike a VIA it looks like we */ 498c2ecf20Sopenharmony_ci /* do this by setting the source's interrupt level to zero. */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci for (i = 0; i < OSS_NUM_SOURCES; i++) 528c2ecf20Sopenharmony_ci oss->irq_level[i] = 0; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* 568c2ecf20Sopenharmony_ci * Handle OSS interrupts. 578c2ecf20Sopenharmony_ci * XXX how do you clear a pending IRQ? is it even necessary? 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic void oss_iopism_irq(struct irq_desc *desc) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci generic_handle_irq(IRQ_MAC_ADB); 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic void oss_scsi_irq(struct irq_desc *desc) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci generic_handle_irq(IRQ_MAC_SCSI); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic void oss_nubus_irq(struct irq_desc *desc) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci u16 events, irq_bit; 738c2ecf20Sopenharmony_ci int irq_num; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci events = oss->irq_pending & OSS_IP_NUBUS; 768c2ecf20Sopenharmony_ci irq_num = NUBUS_SOURCE_BASE + 5; 778c2ecf20Sopenharmony_ci irq_bit = OSS_IP_NUBUS5; 788c2ecf20Sopenharmony_ci do { 798c2ecf20Sopenharmony_ci if (events & irq_bit) { 808c2ecf20Sopenharmony_ci events &= ~irq_bit; 818c2ecf20Sopenharmony_ci generic_handle_irq(irq_num); 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci --irq_num; 848c2ecf20Sopenharmony_ci irq_bit >>= 1; 858c2ecf20Sopenharmony_ci } while (events); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic void oss_iopscc_irq(struct irq_desc *desc) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci generic_handle_irq(IRQ_MAC_SCC); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* 948c2ecf20Sopenharmony_ci * Register the OSS and NuBus interrupt dispatchers. 958c2ecf20Sopenharmony_ci * 968c2ecf20Sopenharmony_ci * This IRQ mapping is laid out with two things in mind: first, we try to keep 978c2ecf20Sopenharmony_ci * things on their own levels to avoid having to do double-dispatches. Second, 988c2ecf20Sopenharmony_ci * the levels match as closely as possible the alternate IRQ mapping mode (aka 998c2ecf20Sopenharmony_ci * "A/UX mode") available on some VIA machines. 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci#define OSS_IRQLEV_IOPISM IRQ_AUTO_1 1038c2ecf20Sopenharmony_ci#define OSS_IRQLEV_SCSI IRQ_AUTO_2 1048c2ecf20Sopenharmony_ci#define OSS_IRQLEV_NUBUS IRQ_AUTO_3 1058c2ecf20Sopenharmony_ci#define OSS_IRQLEV_IOPSCC IRQ_AUTO_4 1068c2ecf20Sopenharmony_ci#define OSS_IRQLEV_VIA1 IRQ_AUTO_6 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_civoid __init oss_register_interrupts(void) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci irq_set_chained_handler(OSS_IRQLEV_IOPISM, oss_iopism_irq); 1118c2ecf20Sopenharmony_ci irq_set_chained_handler(OSS_IRQLEV_SCSI, oss_scsi_irq); 1128c2ecf20Sopenharmony_ci irq_set_chained_handler(OSS_IRQLEV_NUBUS, oss_nubus_irq); 1138c2ecf20Sopenharmony_ci irq_set_chained_handler(OSS_IRQLEV_IOPSCC, oss_iopscc_irq); 1148c2ecf20Sopenharmony_ci irq_set_chained_handler(OSS_IRQLEV_VIA1, via1_irq); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* OSS_VIA1 gets enabled here because it has no machspec interrupt. */ 1178c2ecf20Sopenharmony_ci oss->irq_level[OSS_VIA1] = OSS_IRQLEV_VIA1; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/* 1218c2ecf20Sopenharmony_ci * Enable an OSS interrupt 1228c2ecf20Sopenharmony_ci * 1238c2ecf20Sopenharmony_ci * It looks messy but it's rather straightforward. The switch() statement 1248c2ecf20Sopenharmony_ci * just maps the machspec interrupt numbers to the right OSS interrupt 1258c2ecf20Sopenharmony_ci * source (if the OSS handles that interrupt) and then sets the interrupt 1268c2ecf20Sopenharmony_ci * level for that source to nonzero, thus enabling the interrupt. 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_civoid oss_irq_enable(int irq) { 1308c2ecf20Sopenharmony_ci switch(irq) { 1318c2ecf20Sopenharmony_ci case IRQ_MAC_SCC: 1328c2ecf20Sopenharmony_ci oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_IOPSCC; 1338c2ecf20Sopenharmony_ci return; 1348c2ecf20Sopenharmony_ci case IRQ_MAC_ADB: 1358c2ecf20Sopenharmony_ci oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_IOPISM; 1368c2ecf20Sopenharmony_ci return; 1378c2ecf20Sopenharmony_ci case IRQ_MAC_SCSI: 1388c2ecf20Sopenharmony_ci oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI; 1398c2ecf20Sopenharmony_ci return; 1408c2ecf20Sopenharmony_ci case IRQ_NUBUS_9: 1418c2ecf20Sopenharmony_ci case IRQ_NUBUS_A: 1428c2ecf20Sopenharmony_ci case IRQ_NUBUS_B: 1438c2ecf20Sopenharmony_ci case IRQ_NUBUS_C: 1448c2ecf20Sopenharmony_ci case IRQ_NUBUS_D: 1458c2ecf20Sopenharmony_ci case IRQ_NUBUS_E: 1468c2ecf20Sopenharmony_ci irq -= NUBUS_SOURCE_BASE; 1478c2ecf20Sopenharmony_ci oss->irq_level[irq] = OSS_IRQLEV_NUBUS; 1488c2ecf20Sopenharmony_ci return; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (IRQ_SRC(irq) == 1) 1528c2ecf20Sopenharmony_ci via_irq_enable(irq); 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/* 1568c2ecf20Sopenharmony_ci * Disable an OSS interrupt 1578c2ecf20Sopenharmony_ci * 1588c2ecf20Sopenharmony_ci * Same as above except we set the source's interrupt level to zero, 1598c2ecf20Sopenharmony_ci * to disable the interrupt. 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_civoid oss_irq_disable(int irq) { 1638c2ecf20Sopenharmony_ci switch(irq) { 1648c2ecf20Sopenharmony_ci case IRQ_MAC_SCC: 1658c2ecf20Sopenharmony_ci oss->irq_level[OSS_IOPSCC] = 0; 1668c2ecf20Sopenharmony_ci return; 1678c2ecf20Sopenharmony_ci case IRQ_MAC_ADB: 1688c2ecf20Sopenharmony_ci oss->irq_level[OSS_IOPISM] = 0; 1698c2ecf20Sopenharmony_ci return; 1708c2ecf20Sopenharmony_ci case IRQ_MAC_SCSI: 1718c2ecf20Sopenharmony_ci oss->irq_level[OSS_SCSI] = 0; 1728c2ecf20Sopenharmony_ci return; 1738c2ecf20Sopenharmony_ci case IRQ_NUBUS_9: 1748c2ecf20Sopenharmony_ci case IRQ_NUBUS_A: 1758c2ecf20Sopenharmony_ci case IRQ_NUBUS_B: 1768c2ecf20Sopenharmony_ci case IRQ_NUBUS_C: 1778c2ecf20Sopenharmony_ci case IRQ_NUBUS_D: 1788c2ecf20Sopenharmony_ci case IRQ_NUBUS_E: 1798c2ecf20Sopenharmony_ci irq -= NUBUS_SOURCE_BASE; 1808c2ecf20Sopenharmony_ci oss->irq_level[irq] = 0; 1818c2ecf20Sopenharmony_ci return; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if (IRQ_SRC(irq) == 1) 1858c2ecf20Sopenharmony_ci via_irq_disable(irq); 1868c2ecf20Sopenharmony_ci} 187