cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

oss.c (4576B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 *	Operating System Services (OSS) chip handling
      4 *	Written by Joshua M. Thompson (funaho@jurai.org)
      5 *
      6 *
      7 *	This chip is used in the IIfx in place of VIA #2. It acts like a fancy
      8 *	VIA chip with prorammable interrupt levels.
      9 *
     10 * 990502 (jmt) - Major rewrite for new interrupt architecture as well as some
     11 *		  recent insights into OSS operational details.
     12 * 990610 (jmt) - Now taking full advantage of the OSS. Interrupts are mapped
     13 *		  to mostly match the A/UX interrupt scheme supported on the
     14 *		  VIA side. Also added support for enabling the ISM irq again
     15 *		  since we now have a functional IOP manager.
     16 */
     17
     18#include <linux/types.h>
     19#include <linux/kernel.h>
     20#include <linux/mm.h>
     21#include <linux/delay.h>
     22#include <linux/init.h>
     23#include <linux/irq.h>
     24
     25#include <asm/macintosh.h>
     26#include <asm/macints.h>
     27#include <asm/mac_via.h>
     28#include <asm/mac_oss.h>
     29
     30int oss_present;
     31volatile struct mac_oss *oss;
     32
     33/*
     34 * Initialize the OSS
     35 */
     36
     37void __init oss_init(void)
     38{
     39	int i;
     40
     41	if (macintosh_config->ident != MAC_MODEL_IIFX)
     42		return;
     43
     44	oss = (struct mac_oss *) OSS_BASE;
     45	pr_debug("OSS detected at %p", oss);
     46	oss_present = 1;
     47
     48	/* Disable all interrupts. Unlike a VIA it looks like we    */
     49	/* do this by setting the source's interrupt level to zero. */
     50
     51	for (i = 0; i < OSS_NUM_SOURCES; i++)
     52		oss->irq_level[i] = 0;
     53}
     54
     55/*
     56 * Handle OSS interrupts.
     57 * XXX how do you clear a pending IRQ? is it even necessary?
     58 */
     59
     60static void oss_iopism_irq(struct irq_desc *desc)
     61{
     62	generic_handle_irq(IRQ_MAC_ADB);
     63}
     64
     65static void oss_scsi_irq(struct irq_desc *desc)
     66{
     67	generic_handle_irq(IRQ_MAC_SCSI);
     68}
     69
     70static void oss_nubus_irq(struct irq_desc *desc)
     71{
     72	u16 events, irq_bit;
     73	int irq_num;
     74
     75	events = oss->irq_pending & OSS_IP_NUBUS;
     76	irq_num = NUBUS_SOURCE_BASE + 5;
     77	irq_bit = OSS_IP_NUBUS5;
     78	do {
     79		if (events & irq_bit) {
     80			events &= ~irq_bit;
     81			generic_handle_irq(irq_num);
     82		}
     83		--irq_num;
     84		irq_bit >>= 1;
     85	} while (events);
     86}
     87
     88static void oss_iopscc_irq(struct irq_desc *desc)
     89{
     90	generic_handle_irq(IRQ_MAC_SCC);
     91}
     92
     93/*
     94 * Register the OSS and NuBus interrupt dispatchers.
     95 *
     96 * This IRQ mapping is laid out with two things in mind: first, we try to keep
     97 * things on their own levels to avoid having to do double-dispatches. Second,
     98 * the levels match as closely as possible the alternate IRQ mapping mode (aka
     99 * "A/UX mode") available on some VIA machines.
    100 */
    101
    102#define OSS_IRQLEV_IOPISM    IRQ_AUTO_1
    103#define OSS_IRQLEV_SCSI      IRQ_AUTO_2
    104#define OSS_IRQLEV_NUBUS     IRQ_AUTO_3
    105#define OSS_IRQLEV_IOPSCC    IRQ_AUTO_4
    106#define OSS_IRQLEV_VIA1      IRQ_AUTO_6
    107
    108void __init oss_register_interrupts(void)
    109{
    110	irq_set_chained_handler(OSS_IRQLEV_IOPISM, oss_iopism_irq);
    111	irq_set_chained_handler(OSS_IRQLEV_SCSI,   oss_scsi_irq);
    112	irq_set_chained_handler(OSS_IRQLEV_NUBUS,  oss_nubus_irq);
    113	irq_set_chained_handler(OSS_IRQLEV_IOPSCC, oss_iopscc_irq);
    114	irq_set_chained_handler(OSS_IRQLEV_VIA1,   via1_irq);
    115
    116	/* OSS_VIA1 gets enabled here because it has no machspec interrupt. */
    117	oss->irq_level[OSS_VIA1] = OSS_IRQLEV_VIA1;
    118}
    119
    120/*
    121 * Enable an OSS interrupt
    122 *
    123 * It looks messy but it's rather straightforward. The switch() statement
    124 * just maps the machspec interrupt numbers to the right OSS interrupt
    125 * source (if the OSS handles that interrupt) and then sets the interrupt
    126 * level for that source to nonzero, thus enabling the interrupt.
    127 */
    128
    129void oss_irq_enable(int irq) {
    130	switch(irq) {
    131		case IRQ_MAC_SCC:
    132			oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_IOPSCC;
    133			return;
    134		case IRQ_MAC_ADB:
    135			oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_IOPISM;
    136			return;
    137		case IRQ_MAC_SCSI:
    138			oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI;
    139			return;
    140		case IRQ_NUBUS_9:
    141		case IRQ_NUBUS_A:
    142		case IRQ_NUBUS_B:
    143		case IRQ_NUBUS_C:
    144		case IRQ_NUBUS_D:
    145		case IRQ_NUBUS_E:
    146			irq -= NUBUS_SOURCE_BASE;
    147			oss->irq_level[irq] = OSS_IRQLEV_NUBUS;
    148			return;
    149	}
    150
    151	if (IRQ_SRC(irq) == 1)
    152		via_irq_enable(irq);
    153}
    154
    155/*
    156 * Disable an OSS interrupt
    157 *
    158 * Same as above except we set the source's interrupt level to zero,
    159 * to disable the interrupt.
    160 */
    161
    162void oss_irq_disable(int irq) {
    163	switch(irq) {
    164		case IRQ_MAC_SCC:
    165			oss->irq_level[OSS_IOPSCC] = 0;
    166			return;
    167		case IRQ_MAC_ADB:
    168			oss->irq_level[OSS_IOPISM] = 0;
    169			return;
    170		case IRQ_MAC_SCSI:
    171			oss->irq_level[OSS_SCSI] = 0;
    172			return;
    173		case IRQ_NUBUS_9:
    174		case IRQ_NUBUS_A:
    175		case IRQ_NUBUS_B:
    176		case IRQ_NUBUS_C:
    177		case IRQ_NUBUS_D:
    178		case IRQ_NUBUS_E:
    179			irq -= NUBUS_SOURCE_BASE;
    180			oss->irq_level[irq] = 0;
    181			return;
    182	}
    183
    184	if (IRQ_SRC(irq) == 1)
    185		via_irq_disable(irq);
    186}