1/* $NetBSD: pic_pi.c,v 1.1 2026/01/09 22:54:29 jmcneill Exp $ */
2
3/*-
4 * Copyright (c) 2024-2025 Jared McNeill <jmcneill@invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/*
30 * Processor interface interrupt controller. Top level controller for all
31 * EXT interrupts.
32 */
33
34#include <sys/cdefs.h>
35__KERNEL_RCSID(0, "$NetBSD: pic_pi.c,v 1.1 2026/01/09 22:54:29 jmcneill Exp $");
36
37#include "opt_multiprocessor.h"
38
39#include <sys/param.h>
40#include <sys/intr.h>
41#include <sys/systm.h>
42#include <sys/bus.h>
43#include <sys/bitops.h>
44#include <sys/cpu.h>
45#include <powerpc/include/spr.h>
46#include <powerpc/include/oea/spr.h>
47#include <machine/pio.h>
48#include <machine/intr.h>
49#include <arch/powerpc/pic/picvar.h>
50#include <arch/powerpc/pic/ipivar.h>
51#include <machine/wii.h>
52#include <machine/wiiu.h>
53
54#include "pic_pi.h"
55
56struct pic_state {
57	uint32_t irqmask;
58	uint32_t actmask;
59	uint32_t intsr;
60	uint32_t intmr;
61} __aligned(32);
62
63static struct pic_state pic_s[CPU_MAXNUM];
64
65#define WR4(reg, val)	out32(reg, val)
66#define RD4(reg)	in32(reg)
67
68#ifdef MULTIPROCESSOR
69extern struct ipi_ops ipiops;
70
71#define IRQ_IS_IPI(_irq)	\
72    ((_irq) >= WIIU_PI_IRQ_MB_CPU(0) && (_irq) <= WIIU_PI_IRQ_MB_CPU(2))
73#endif
74
75static u_int
76pi_irq_affinity(int irq)
77{
78#ifdef MULTIPROCESSOR
79	if (wiiu_native && IRQ_IS_IPI(irq)) {
80		return irq - WIIU_PI_IRQ_MB_CPU(0);
81	}
82#endif
83	return 0;
84}
85
86static void
87pi_enable_irq(struct pic_ops *pic, int irq, int type)
88{
89	const u_int cpu_num = pi_irq_affinity(irq);
90
91	pic_s[cpu_num].irqmask |= __BIT(irq);
92	WR4(pic_s[cpu_num].intmr, pic_s[cpu_num].irqmask & ~pic_s[cpu_num].actmask);
93}
94
95static void
96pi_disable_irq(struct pic_ops *pic, int irq)
97{
98	const u_int cpu_num = pi_irq_affinity(irq);
99
100	pic_s[cpu_num].irqmask &= ~__BIT(irq);
101	WR4(pic_s[cpu_num].intmr, pic_s[cpu_num].irqmask & ~pic_s[cpu_num].actmask);
102}
103
104#ifdef MULTIPROCESSOR
105static void
106pi_ipi_ack(const u_int cpu_num, register_t spr)
107{
108	do {
109		mtspr(SPR_SCR, spr & ~SPR_SCR_IPI_PEND(cpu_num));
110		spr = mfspr(SPR_SCR);
111	} while ((spr & SPR_SCR_IPI_PEND(cpu_num)) != 0);
112}
113#endif
114
115static int
116pi_get_irq(struct pic_ops *pic, int mode)
117{
118	const u_int cpu_num = cpu_number();
119	uint32_t raw, pend;
120	int irq;
121
122#ifdef MULTIPROCESSOR
123	if (wiiu_native) {
124		register_t spr = mfspr(SPR_SCR);
125
126		if ((spr & SPR_SCR_IPI_PEND(cpu_num)) != 0) {
127			pi_ipi_ack(cpu_num, spr);
128			return WIIU_PI_IRQ_MB_CPU(cpu_num);
129		}
130	}
131#endif
132
133	raw = RD4(pic_s[cpu_num].intsr);
134	pend = raw & pic_s[cpu_num].irqmask;
135	if (pend == 0) {
136		return 255;
137	}
138	irq = ffs32(pend) - 1;
139
140	pic_s[cpu_num].actmask |= __BIT(irq);
141	WR4(pic_s[cpu_num].intmr, pic_s[cpu_num].irqmask & ~pic_s[cpu_num].actmask);
142
143	return irq;
144}
145
146static void
147pi_ack_irq(struct pic_ops *pic, int irq)
148{
149	const u_int cpu_num = cpu_number();
150
151	pic_s[cpu_num].actmask &= ~__BIT(irq);
152	WR4(pic_s[cpu_num].intmr, pic_s[cpu_num].irqmask & ~pic_s[cpu_num].actmask);
153	WR4(pic_s[cpu_num].intsr, __BIT(irq));
154}
155
156static struct pic_ops pic = {
157	.pic_name = "pi",
158	.pic_numintrs = 32,
159	.pic_cookie = NULL,
160	.pic_enable_irq = pi_enable_irq,
161	.pic_reenable_irq = pi_enable_irq,
162	.pic_disable_irq = pi_disable_irq,
163	.pic_get_irq = pi_get_irq,
164	.pic_ack_irq = pi_ack_irq,
165	.pic_establish_irq = dummy_pic_establish_intr,
166};
167
168void
169pi_init_intr(void)
170{
171	u_int cpu_num;
172
173	for (cpu_num = 0; cpu_num < uimin(3, CPU_MAXNUM); cpu_num++) {
174		pic_s[cpu_num].irqmask = 0;
175		pic_s[cpu_num].actmask = 0;
176		if (wiiu_native) {
177			pic_s[cpu_num].intmr = WIIU_PI_INTMSK(cpu_num);
178			pic_s[cpu_num].intsr = WIIU_PI_INTSR(cpu_num);
179		} else {
180			pic_s[cpu_num].intmr = PI_INTMR;
181			pic_s[cpu_num].intsr = PI_INTSR;
182		}
183
184		/* Mask and clear all interrupts. */
185		WR4(pic_s[cpu_num].intmr, 0);
186		WR4(pic_s[cpu_num].intsr, ~0U);
187	}
188
189	pic_add(&pic);
190}
191