18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * arch/sh/boards/dreamcast/irq.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Holly IRQ support for the Sega Dreamcast.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (c) 2001, 2002 M. R. Brown <mrbrown@0xd6.org>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * This file is part of the LinuxDC project (www.linuxdc.org)
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci#include <linux/irq.h>
128c2ecf20Sopenharmony_ci#include <linux/io.h>
138c2ecf20Sopenharmony_ci#include <linux/export.h>
148c2ecf20Sopenharmony_ci#include <linux/err.h>
158c2ecf20Sopenharmony_ci#include <mach/sysasic.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/*
188c2ecf20Sopenharmony_ci * Dreamcast System ASIC Hardware Events -
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * The Dreamcast's System ASIC (a.k.a. Holly) is responsible for receiving
218c2ecf20Sopenharmony_ci * hardware events from system peripherals and triggering an SH7750 IRQ.
228c2ecf20Sopenharmony_ci * Hardware events can trigger IRQs 13, 11, or 9 depending on which bits are
238c2ecf20Sopenharmony_ci * set in the Event Mask Registers (EMRs).  When a hardware event is
248c2ecf20Sopenharmony_ci * triggered, its corresponding bit in the Event Status Registers (ESRs)
258c2ecf20Sopenharmony_ci * is set, and that bit should be rewritten to the ESR to acknowledge that
268c2ecf20Sopenharmony_ci * event.
278c2ecf20Sopenharmony_ci *
288c2ecf20Sopenharmony_ci * There are three 32-bit ESRs located at 0xa05f6900 - 0xa05f6908.  Event
298c2ecf20Sopenharmony_ci * types can be found in arch/sh/include/mach-dreamcast/mach/sysasic.h.
308c2ecf20Sopenharmony_ci * There are three groups of EMRs that parallel the ESRs.  Each EMR group
318c2ecf20Sopenharmony_ci * corresponds to an IRQ, so 0xa05f6910 - 0xa05f6918 triggers IRQ 13,
328c2ecf20Sopenharmony_ci * 0xa05f6920 - 0xa05f6928 triggers IRQ 11, and 0xa05f6930 - 0xa05f6938
338c2ecf20Sopenharmony_ci * triggers IRQ 9.
348c2ecf20Sopenharmony_ci *
358c2ecf20Sopenharmony_ci * In the kernel, these events are mapped to virtual IRQs so that drivers can
368c2ecf20Sopenharmony_ci * respond to them as they would a normal interrupt.  In order to keep this
378c2ecf20Sopenharmony_ci * mapping simple, the events are mapped as:
388c2ecf20Sopenharmony_ci *
398c2ecf20Sopenharmony_ci * 6900/6910 - Events  0-31, IRQ 13
408c2ecf20Sopenharmony_ci * 6904/6924 - Events 32-63, IRQ 11
418c2ecf20Sopenharmony_ci * 6908/6938 - Events 64-95, IRQ  9
428c2ecf20Sopenharmony_ci *
438c2ecf20Sopenharmony_ci */
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define ESR_BASE 0x005f6900    /* Base event status register */
468c2ecf20Sopenharmony_ci#define EMR_BASE 0x005f6910    /* Base event mask register */
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/*
498c2ecf20Sopenharmony_ci * Helps us determine the EMR group that this event belongs to: 0 = 0x6910,
508c2ecf20Sopenharmony_ci * 1 = 0x6920, 2 = 0x6930; also determine the event offset.
518c2ecf20Sopenharmony_ci */
528c2ecf20Sopenharmony_ci#define LEVEL(event) (((event) - HW_EVENT_IRQ_BASE) / 32)
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/* Return the hardware event's bit position within the EMR/ESR */
558c2ecf20Sopenharmony_ci#define EVENT_BIT(event) (((event) - HW_EVENT_IRQ_BASE) & 31)
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/*
588c2ecf20Sopenharmony_ci * For each of these *_irq routines, the IRQ passed in is the virtual IRQ
598c2ecf20Sopenharmony_ci * (logically mapped to the corresponding bit for the hardware event).
608c2ecf20Sopenharmony_ci */
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/* Disable the hardware event by masking its bit in its EMR */
638c2ecf20Sopenharmony_cistatic inline void disable_systemasic_irq(struct irq_data *data)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	unsigned int irq = data->irq;
668c2ecf20Sopenharmony_ci	__u32 emr = EMR_BASE + (LEVEL(irq) << 4) + (LEVEL(irq) << 2);
678c2ecf20Sopenharmony_ci	__u32 mask;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	mask = inl(emr);
708c2ecf20Sopenharmony_ci	mask &= ~(1 << EVENT_BIT(irq));
718c2ecf20Sopenharmony_ci	outl(mask, emr);
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/* Enable the hardware event by setting its bit in its EMR */
758c2ecf20Sopenharmony_cistatic inline void enable_systemasic_irq(struct irq_data *data)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	unsigned int irq = data->irq;
788c2ecf20Sopenharmony_ci	__u32 emr = EMR_BASE + (LEVEL(irq) << 4) + (LEVEL(irq) << 2);
798c2ecf20Sopenharmony_ci	__u32 mask;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	mask = inl(emr);
828c2ecf20Sopenharmony_ci	mask |= (1 << EVENT_BIT(irq));
838c2ecf20Sopenharmony_ci	outl(mask, emr);
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci/* Acknowledge a hardware event by writing its bit back to its ESR */
878c2ecf20Sopenharmony_cistatic void mask_ack_systemasic_irq(struct irq_data *data)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	unsigned int irq = data->irq;
908c2ecf20Sopenharmony_ci	__u32 esr = ESR_BASE + (LEVEL(irq) << 2);
918c2ecf20Sopenharmony_ci	disable_systemasic_irq(data);
928c2ecf20Sopenharmony_ci	outl((1 << EVENT_BIT(irq)), esr);
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistruct irq_chip systemasic_int = {
968c2ecf20Sopenharmony_ci	.name		= "System ASIC",
978c2ecf20Sopenharmony_ci	.irq_mask	= disable_systemasic_irq,
988c2ecf20Sopenharmony_ci	.irq_mask_ack	= mask_ack_systemasic_irq,
998c2ecf20Sopenharmony_ci	.irq_unmask	= enable_systemasic_irq,
1008c2ecf20Sopenharmony_ci};
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci/*
1038c2ecf20Sopenharmony_ci * Map the hardware event indicated by the processor IRQ to a virtual IRQ.
1048c2ecf20Sopenharmony_ci */
1058c2ecf20Sopenharmony_ciint systemasic_irq_demux(int irq)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	__u32 emr, esr, status, level;
1088c2ecf20Sopenharmony_ci	__u32 j, bit;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	switch (irq) {
1118c2ecf20Sopenharmony_ci	case 13:
1128c2ecf20Sopenharmony_ci		level = 0;
1138c2ecf20Sopenharmony_ci		break;
1148c2ecf20Sopenharmony_ci	case 11:
1158c2ecf20Sopenharmony_ci		level = 1;
1168c2ecf20Sopenharmony_ci		break;
1178c2ecf20Sopenharmony_ci	case  9:
1188c2ecf20Sopenharmony_ci		level = 2;
1198c2ecf20Sopenharmony_ci		break;
1208c2ecf20Sopenharmony_ci	default:
1218c2ecf20Sopenharmony_ci		return irq;
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci	emr = EMR_BASE + (level << 4) + (level << 2);
1248c2ecf20Sopenharmony_ci	esr = ESR_BASE + (level << 2);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	/* Mask the ESR to filter any spurious, unwanted interrupts */
1278c2ecf20Sopenharmony_ci	status = inl(esr);
1288c2ecf20Sopenharmony_ci	status &= inl(emr);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	/* Now scan and find the first set bit as the event to map */
1318c2ecf20Sopenharmony_ci	for (bit = 1, j = 0; j < 32; bit <<= 1, j++) {
1328c2ecf20Sopenharmony_ci		if (status & bit) {
1338c2ecf20Sopenharmony_ci			irq = HW_EVENT_IRQ_BASE + j + (level << 5);
1348c2ecf20Sopenharmony_ci			return irq;
1358c2ecf20Sopenharmony_ci		}
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	/* Not reached */
1398c2ecf20Sopenharmony_ci	return irq;
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_civoid systemasic_irq_init(void)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	int irq_base, i;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	irq_base = irq_alloc_descs(HW_EVENT_IRQ_BASE, HW_EVENT_IRQ_BASE,
1478c2ecf20Sopenharmony_ci				   HW_EVENT_IRQ_MAX - HW_EVENT_IRQ_BASE, -1);
1488c2ecf20Sopenharmony_ci	if (IS_ERR_VALUE(irq_base)) {
1498c2ecf20Sopenharmony_ci		pr_err("%s: failed hooking irqs\n", __func__);
1508c2ecf20Sopenharmony_ci		return;
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	for (i = HW_EVENT_IRQ_BASE; i < HW_EVENT_IRQ_MAX; i++)
1548c2ecf20Sopenharmony_ci		irq_set_chip_and_handler(i, &systemasic_int, handle_level_irq);
1558c2ecf20Sopenharmony_ci}
156