1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 *
4 * Copyright (c) 2004 MIPS Inc
5 * Author: chris@mips.com
6 *
7 * Copyright (C) 2004, 06 Ralf Baechle <ralf@linux-mips.org>
8 */
9#include <linux/interrupt.h>
10#include <linux/kernel.h>
11#include <linux/sched.h>
12#include <linux/kernel_stat.h>
13#include <asm/io.h>
14#include <asm/irq.h>
15#include <asm/msc01_ic.h>
16#include <asm/traps.h>
17
18static unsigned long _icctrl_msc;
19#define MSC01_IC_REG_BASE	_icctrl_msc
20
21#define MSCIC_WRITE(reg, data)	do { *(volatile u32 *)(reg) = data; } while (0)
22#define MSCIC_READ(reg, data)	do { data = *(volatile u32 *)(reg); } while (0)
23
24static unsigned int irq_base;
25
26/* mask off an interrupt */
27static inline void mask_msc_irq(struct irq_data *d)
28{
29	unsigned int irq = d->irq;
30
31	if (irq < (irq_base + 32))
32		MSCIC_WRITE(MSC01_IC_DISL, 1<<(irq - irq_base));
33	else
34		MSCIC_WRITE(MSC01_IC_DISH, 1<<(irq - irq_base - 32));
35}
36
37/* unmask an interrupt */
38static inline void unmask_msc_irq(struct irq_data *d)
39{
40	unsigned int irq = d->irq;
41
42	if (irq < (irq_base + 32))
43		MSCIC_WRITE(MSC01_IC_ENAL, 1<<(irq - irq_base));
44	else
45		MSCIC_WRITE(MSC01_IC_ENAH, 1<<(irq - irq_base - 32));
46}
47
48/*
49 * Masks and ACKs an IRQ
50 */
51static void level_mask_and_ack_msc_irq(struct irq_data *d)
52{
53	mask_msc_irq(d);
54	if (!cpu_has_veic)
55		MSCIC_WRITE(MSC01_IC_EOI, 0);
56}
57
58/*
59 * Masks and ACKs an IRQ
60 */
61static void edge_mask_and_ack_msc_irq(struct irq_data *d)
62{
63	unsigned int irq = d->irq;
64
65	mask_msc_irq(d);
66	if (!cpu_has_veic)
67		MSCIC_WRITE(MSC01_IC_EOI, 0);
68	else {
69		u32 r;
70		MSCIC_READ(MSC01_IC_SUP+irq*8, r);
71		MSCIC_WRITE(MSC01_IC_SUP+irq*8, r | ~MSC01_IC_SUP_EDGE_BIT);
72		MSCIC_WRITE(MSC01_IC_SUP+irq*8, r);
73	}
74}
75
76/*
77 * Interrupt handler for interrupts coming from SOC-it.
78 */
79void ll_msc_irq(void)
80{
81	unsigned int irq;
82
83	/* read the interrupt vector register */
84	MSCIC_READ(MSC01_IC_VEC, irq);
85	if (irq < 64)
86		do_IRQ(irq + irq_base);
87	else {
88		/* Ignore spurious interrupt */
89	}
90}
91
92static void msc_bind_eic_interrupt(int irq, int set)
93{
94	MSCIC_WRITE(MSC01_IC_RAMW,
95		    (irq<<MSC01_IC_RAMW_ADDR_SHF) | (set<<MSC01_IC_RAMW_DATA_SHF));
96}
97
98static struct irq_chip msc_levelirq_type = {
99	.name = "SOC-it-Level",
100	.irq_ack = level_mask_and_ack_msc_irq,
101	.irq_mask = mask_msc_irq,
102	.irq_mask_ack = level_mask_and_ack_msc_irq,
103	.irq_unmask = unmask_msc_irq,
104	.irq_eoi = unmask_msc_irq,
105};
106
107static struct irq_chip msc_edgeirq_type = {
108	.name = "SOC-it-Edge",
109	.irq_ack = edge_mask_and_ack_msc_irq,
110	.irq_mask = mask_msc_irq,
111	.irq_mask_ack = edge_mask_and_ack_msc_irq,
112	.irq_unmask = unmask_msc_irq,
113	.irq_eoi = unmask_msc_irq,
114};
115
116
117void __init init_msc_irqs(unsigned long icubase, unsigned int irqbase, msc_irqmap_t *imp, int nirq)
118{
119	_icctrl_msc = (unsigned long) ioremap(icubase, 0x40000);
120
121	/* Reset interrupt controller - initialises all registers to 0 */
122	MSCIC_WRITE(MSC01_IC_RST, MSC01_IC_RST_RST_BIT);
123
124	board_bind_eic_interrupt = &msc_bind_eic_interrupt;
125
126	for (; nirq > 0; nirq--, imp++) {
127		int n = imp->im_irq;
128
129		switch (imp->im_type) {
130		case MSC01_IRQ_EDGE:
131			irq_set_chip_and_handler_name(irqbase + n,
132						      &msc_edgeirq_type,
133						      handle_edge_irq,
134						      "edge");
135			if (cpu_has_veic)
136				MSCIC_WRITE(MSC01_IC_SUP+n*8, MSC01_IC_SUP_EDGE_BIT);
137			else
138				MSCIC_WRITE(MSC01_IC_SUP+n*8, MSC01_IC_SUP_EDGE_BIT | imp->im_lvl);
139			break;
140		case MSC01_IRQ_LEVEL:
141			irq_set_chip_and_handler_name(irqbase + n,
142						      &msc_levelirq_type,
143						      handle_level_irq,
144						      "level");
145			if (cpu_has_veic)
146				MSCIC_WRITE(MSC01_IC_SUP+n*8, 0);
147			else
148				MSCIC_WRITE(MSC01_IC_SUP+n*8, imp->im_lvl);
149		}
150	}
151
152	irq_base = irqbase;
153
154	MSCIC_WRITE(MSC01_IC_GENA, MSC01_IC_GENA_GENA_BIT);	/* Enable interrupt generation */
155
156}
157