162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * arch/sh/boards/dreamcast/irq.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Holly IRQ support for the Sega Dreamcast.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (c) 2001, 2002 M. R. Brown <mrbrown@0xd6.org>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * This file is part of the LinuxDC project (www.linuxdc.org)
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci#include <linux/irq.h>
1262306a36Sopenharmony_ci#include <linux/io.h>
1362306a36Sopenharmony_ci#include <linux/export.h>
1462306a36Sopenharmony_ci#include <linux/err.h>
1562306a36Sopenharmony_ci#include <mach/sysasic.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/*
1862306a36Sopenharmony_ci * Dreamcast System ASIC Hardware Events -
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * The Dreamcast's System ASIC (a.k.a. Holly) is responsible for receiving
2162306a36Sopenharmony_ci * hardware events from system peripherals and triggering an SH7750 IRQ.
2262306a36Sopenharmony_ci * Hardware events can trigger IRQs 13, 11, or 9 depending on which bits are
2362306a36Sopenharmony_ci * set in the Event Mask Registers (EMRs).  When a hardware event is
2462306a36Sopenharmony_ci * triggered, its corresponding bit in the Event Status Registers (ESRs)
2562306a36Sopenharmony_ci * is set, and that bit should be rewritten to the ESR to acknowledge that
2662306a36Sopenharmony_ci * event.
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci * There are three 32-bit ESRs located at 0xa05f6900 - 0xa05f6908.  Event
2962306a36Sopenharmony_ci * types can be found in arch/sh/include/mach-dreamcast/mach/sysasic.h.
3062306a36Sopenharmony_ci * There are three groups of EMRs that parallel the ESRs.  Each EMR group
3162306a36Sopenharmony_ci * corresponds to an IRQ, so 0xa05f6910 - 0xa05f6918 triggers IRQ 13,
3262306a36Sopenharmony_ci * 0xa05f6920 - 0xa05f6928 triggers IRQ 11, and 0xa05f6930 - 0xa05f6938
3362306a36Sopenharmony_ci * triggers IRQ 9.
3462306a36Sopenharmony_ci *
3562306a36Sopenharmony_ci * In the kernel, these events are mapped to virtual IRQs so that drivers can
3662306a36Sopenharmony_ci * respond to them as they would a normal interrupt.  In order to keep this
3762306a36Sopenharmony_ci * mapping simple, the events are mapped as:
3862306a36Sopenharmony_ci *
3962306a36Sopenharmony_ci * 6900/6910 - Events  0-31, IRQ 13
4062306a36Sopenharmony_ci * 6904/6924 - Events 32-63, IRQ 11
4162306a36Sopenharmony_ci * 6908/6938 - Events 64-95, IRQ  9
4262306a36Sopenharmony_ci *
4362306a36Sopenharmony_ci */
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define ESR_BASE 0x005f6900    /* Base event status register */
4662306a36Sopenharmony_ci#define EMR_BASE 0x005f6910    /* Base event mask register */
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/*
4962306a36Sopenharmony_ci * Helps us determine the EMR group that this event belongs to: 0 = 0x6910,
5062306a36Sopenharmony_ci * 1 = 0x6920, 2 = 0x6930; also determine the event offset.
5162306a36Sopenharmony_ci */
5262306a36Sopenharmony_ci#define LEVEL(event) (((event) - HW_EVENT_IRQ_BASE) / 32)
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/* Return the hardware event's bit position within the EMR/ESR */
5562306a36Sopenharmony_ci#define EVENT_BIT(event) (((event) - HW_EVENT_IRQ_BASE) & 31)
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/*
5862306a36Sopenharmony_ci * For each of these *_irq routines, the IRQ passed in is the virtual IRQ
5962306a36Sopenharmony_ci * (logically mapped to the corresponding bit for the hardware event).
6062306a36Sopenharmony_ci */
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/* Disable the hardware event by masking its bit in its EMR */
6362306a36Sopenharmony_cistatic inline void disable_systemasic_irq(struct irq_data *data)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	unsigned int irq = data->irq;
6662306a36Sopenharmony_ci	__u32 emr = EMR_BASE + (LEVEL(irq) << 4) + (LEVEL(irq) << 2);
6762306a36Sopenharmony_ci	__u32 mask;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	mask = inl(emr);
7062306a36Sopenharmony_ci	mask &= ~(1 << EVENT_BIT(irq));
7162306a36Sopenharmony_ci	outl(mask, emr);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/* Enable the hardware event by setting its bit in its EMR */
7562306a36Sopenharmony_cistatic inline void enable_systemasic_irq(struct irq_data *data)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	unsigned int irq = data->irq;
7862306a36Sopenharmony_ci	__u32 emr = EMR_BASE + (LEVEL(irq) << 4) + (LEVEL(irq) << 2);
7962306a36Sopenharmony_ci	__u32 mask;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	mask = inl(emr);
8262306a36Sopenharmony_ci	mask |= (1 << EVENT_BIT(irq));
8362306a36Sopenharmony_ci	outl(mask, emr);
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci/* Acknowledge a hardware event by writing its bit back to its ESR */
8762306a36Sopenharmony_cistatic void mask_ack_systemasic_irq(struct irq_data *data)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	unsigned int irq = data->irq;
9062306a36Sopenharmony_ci	__u32 esr = ESR_BASE + (LEVEL(irq) << 2);
9162306a36Sopenharmony_ci	disable_systemasic_irq(data);
9262306a36Sopenharmony_ci	outl((1 << EVENT_BIT(irq)), esr);
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistruct irq_chip systemasic_int = {
9662306a36Sopenharmony_ci	.name		= "System ASIC",
9762306a36Sopenharmony_ci	.irq_mask	= disable_systemasic_irq,
9862306a36Sopenharmony_ci	.irq_mask_ack	= mask_ack_systemasic_irq,
9962306a36Sopenharmony_ci	.irq_unmask	= enable_systemasic_irq,
10062306a36Sopenharmony_ci};
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci/*
10362306a36Sopenharmony_ci * Map the hardware event indicated by the processor IRQ to a virtual IRQ.
10462306a36Sopenharmony_ci */
10562306a36Sopenharmony_ciint systemasic_irq_demux(int irq)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	__u32 emr, esr, status, level;
10862306a36Sopenharmony_ci	__u32 j, bit;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	switch (irq) {
11162306a36Sopenharmony_ci	case 13 + 16:
11262306a36Sopenharmony_ci		level = 0;
11362306a36Sopenharmony_ci		break;
11462306a36Sopenharmony_ci	case 11 + 16:
11562306a36Sopenharmony_ci		level = 1;
11662306a36Sopenharmony_ci		break;
11762306a36Sopenharmony_ci	case 9 + 16:
11862306a36Sopenharmony_ci		level = 2;
11962306a36Sopenharmony_ci		break;
12062306a36Sopenharmony_ci	default:
12162306a36Sopenharmony_ci		return irq;
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci	emr = EMR_BASE + (level << 4) + (level << 2);
12462306a36Sopenharmony_ci	esr = ESR_BASE + (level << 2);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	/* Mask the ESR to filter any spurious, unwanted interrupts */
12762306a36Sopenharmony_ci	status = inl(esr);
12862306a36Sopenharmony_ci	status &= inl(emr);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	/* Now scan and find the first set bit as the event to map */
13162306a36Sopenharmony_ci	for (bit = 1, j = 0; j < 32; bit <<= 1, j++) {
13262306a36Sopenharmony_ci		if (status & bit) {
13362306a36Sopenharmony_ci			irq = HW_EVENT_IRQ_BASE + j + (level << 5);
13462306a36Sopenharmony_ci			return irq;
13562306a36Sopenharmony_ci		}
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	/* Not reached */
13962306a36Sopenharmony_ci	return irq;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_civoid systemasic_irq_init(void)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	int irq_base, i;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	irq_base = irq_alloc_descs(HW_EVENT_IRQ_BASE, HW_EVENT_IRQ_BASE,
14762306a36Sopenharmony_ci				   HW_EVENT_IRQ_MAX - HW_EVENT_IRQ_BASE, -1);
14862306a36Sopenharmony_ci	if (IS_ERR_VALUE(irq_base)) {
14962306a36Sopenharmony_ci		pr_err("%s: failed hooking irqs\n", __func__);
15062306a36Sopenharmony_ci		return;
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	for (i = HW_EVENT_IRQ_BASE; i < HW_EVENT_IRQ_MAX; i++)
15462306a36Sopenharmony_ci		irq_set_chip_and_handler(i, &systemasic_int, handle_level_irq);
15562306a36Sopenharmony_ci}
156