ahb.c revision 1.1
1/* $NetBSD: ahb.c,v 1.1 2026/01/09 22:54:29 jmcneill Exp $ */
2
3/*-
4 * Copyright (c) 2024 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 AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__KERNEL_RCSID(0, "$NetBSD: ahb.c,v 1.1 2026/01/09 22:54:29 jmcneill Exp $");
31
32#include <sys/param.h>
33#include <sys/bus.h>
34#include <sys/device.h>
35#include <sys/systm.h>
36#include <sys/bitops.h>
37
38#include <machine/intr.h>
39#include <machine/wii.h>
40#include <machine/wiiu.h>
41#include <machine/pio.h>
42#include <powerpc/pic/picvar.h>
43
44#include "locators.h"
45#include "mainbus.h"
46#include "ahb.h"
47
48#define WR4(reg, val)		out32(reg, val)
49#define RD4(reg)		in32(reg)
50
51static struct mainbus_attach_args ahb_maa;
52static uint32_t pic_irqmask[2];
53
54static int	ahb_match(device_t, cfdata_t, void *);
55static void	ahb_attach(device_t, device_t, void *);
56
57static int	ahb_search(device_t, cfdata_t, const int *, void *);
58static int	ahb_print(void *, const char *);
59
60static void	ahb_intr_init(int);
61
62static void	hollywood_enable_irq(struct pic_ops *, int, int);
63static void	hollywood_disable_irq(struct pic_ops *, int);
64static int	hollywood_get_irq(struct pic_ops *, int);
65static void	hollywood_ack_irq(struct pic_ops *, int);
66static void	hollywood_establish_irq(struct pic_ops *, int, int, int);
67
68static struct pic_ops hollywood_pic = {
69	.pic_name = "hollywood",
70	.pic_numintrs = 32,
71	.pic_cookie = NULL,
72	.pic_enable_irq = hollywood_enable_irq,
73	.pic_reenable_irq = hollywood_enable_irq,
74	.pic_disable_irq = hollywood_disable_irq,
75	.pic_get_irq = hollywood_get_irq,
76	.pic_ack_irq = hollywood_ack_irq,
77	.pic_establish_irq = hollywood_establish_irq,
78};
79
80static void	latte_enable_irq(struct pic_ops *, int, int);
81static void	latte_disable_irq(struct pic_ops *, int);
82static int	latte_get_irq(struct pic_ops *, int);
83static void	latte_ack_irq(struct pic_ops *, int);
84static void	latte_establish_irq(struct pic_ops *, int, int, int);
85
86static struct pic_ops latte_pic = {
87	.pic_name = "latte",
88	.pic_numintrs = 64,
89	.pic_cookie = NULL,
90	.pic_enable_irq = latte_enable_irq,
91	.pic_reenable_irq = latte_enable_irq,
92	.pic_disable_irq = latte_disable_irq,
93	.pic_get_irq = latte_get_irq,
94	.pic_ack_irq = latte_ack_irq,
95	.pic_establish_irq = latte_establish_irq,
96};
97
98CFATTACH_DECL_NEW(ahb, 0, ahb_match, ahb_attach, NULL, NULL);
99
100static int
101ahb_match(device_t parent, cfdata_t cf, void *aux)
102{
103	struct mainbus_attach_args *maa = aux;
104
105	return strcmp(maa->maa_name, "ahb") == 0;
106}
107
108static void
109ahb_attach(device_t parent, device_t self, void *aux)
110{
111	uint32_t val;
112
113	ahb_maa = *(struct mainbus_attach_args *)aux;
114
115	aprint_naive("\n");
116	if (wiiu_native) {
117		val = RD4(LT_CHIPREVID);
118
119		aprint_normal(": Latte 0x%02x\n",
120		    (unsigned)(val & (LT_CHIPREVID_VERHI | LT_CHIPREVID_VERLO)));
121	} else {
122		val = RD4(HW_VERSION);
123
124		aprint_normal(": Hollywood ES%u.%u\n",
125		    (unsigned)__SHIFTOUT(val, HWVER_MASK) + 1,
126		    (unsigned)__SHIFTOUT(val, HWREV_MASK));
127	}
128
129	ahb_intr_init(ahb_maa.maa_irq);
130
131	config_search(self, NULL,
132	    CFARGS(.search = ahb_search));
133}
134
135static int
136ahb_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
137{
138	struct ahb_attach_args aaa;
139
140	aaa.aaa_bst = ahb_maa.maa_bst;
141	aaa.aaa_dmat = ahb_maa.maa_dmat;
142	if (cf->cf_loc[AHBCF_ADDR] != AHBCF_ADDR_DEFAULT) {
143		aaa.aaa_addr = cf->cf_loc[AHBCF_ADDR];
144	} else {
145		aaa.aaa_addr = 0;
146	}
147	aaa.aaa_irq = cf->cf_loc[AHBCF_IRQ];
148
149	if (config_probe(parent, cf, &aaa)) {
150		config_attach(parent, cf, &aaa, ahb_print, CFARGS_NONE);
151	}
152
153	return 0;
154}
155
156static int
157ahb_print(void *aux, const char *pnp)
158{
159	struct ahb_attach_args *aaa = aux;
160
161	if (pnp != NULL) {
162		return QUIET;
163	}
164
165	if (aaa->aaa_addr != 0) {
166		aprint_normal(" addr 0x%08lx", aaa->aaa_addr);
167	}
168	if (aaa->aaa_irq != AHBCF_IRQ_DEFAULT) {
169		aprint_normal(" irq %d", aaa->aaa_irq);
170	}
171
172	return UNCONF;
173}
174
175static void
176hollywood_enable_irq(struct pic_ops *pic, int irq, int type)
177{
178	pic_irqmask[0] |= __BIT(irq);
179	WR4(HW_PPCIRQMASK, pic_irqmask[0]);
180}
181
182static void
183hollywood_disable_irq(struct pic_ops *pic, int irq)
184{
185	pic_irqmask[0] &= ~__BIT(irq);
186	WR4(HW_PPCIRQMASK, pic_irqmask[0]);
187}
188
189static int
190hollywood_get_irq(struct pic_ops *pic, int mode)
191{
192	uint32_t raw, pend;
193	int irq;
194
195	raw = RD4(HW_PPCIRQFLAGS);
196	pend = raw & pic_irqmask[0];
197	if (pend == 0) {
198		return 255;
199	}
200	irq = ffs32(pend) - 1;
201
202	return irq;
203}
204
205static void
206hollywood_ack_irq(struct pic_ops *pic, int irq)
207{
208	WR4(HW_PPCIRQFLAGS, __BIT(irq));
209}
210
211static void
212hollywood_establish_irq(struct pic_ops *pic, int irq, int type, int pri)
213{
214	uint32_t val;
215
216	/* Mask and clear Starlet interrupt */
217	val = RD4(HW_ARMIRQMASK);
218	val &= ~__BIT(irq);
219	WR4(HW_ARMIRQMASK, val);
220	WR4(HW_ARMIRQFLAGS, __BIT(irq));
221}
222
223static void
224latte_enable_irq(struct pic_ops *pic, int irq, int type)
225{
226	unsigned reg_en = irq < 32 ? LT_PPCnINT1EN(0) : LT_PPCnINT2EN(0);
227
228	pic_irqmask[irq / 32] |= __BIT(irq % 32);
229	WR4(reg_en, pic_irqmask[irq / 32]);
230}
231
232static void
233latte_disable_irq(struct pic_ops *pic, int irq)
234{
235	unsigned reg_en = irq < 32 ? LT_PPCnINT1EN(0) : LT_PPCnINT2EN(0);
236
237	pic_irqmask[irq / 32] &= ~__BIT(irq % 32);
238	WR4(reg_en, pic_irqmask[irq / 32]);
239}
240
241static int
242latte_get_irq(struct pic_ops *pic, int mode)
243{
244	uint32_t raw, pend;
245	int irq;
246
247	raw = RD4(LT_PPCnINT1STS(0));
248	pend = raw & pic_irqmask[0];
249	if (pend == 0) {
250		raw = RD4(LT_PPCnINT2STS(0));
251		pend = raw & pic_irqmask[1];
252		if (pend == 0) {
253			return 255;
254		}
255		irq = 32 + ffs32(pend) - 1;
256	} else {
257		irq = ffs32(pend) - 1;
258	}
259
260	return irq;
261}
262
263static void
264latte_ack_irq(struct pic_ops *pic, int irq)
265{
266	unsigned reg_sts = irq < 32 ? LT_PPCnINT1STS(0) : LT_PPCnINT2STS(0);
267
268	WR4(reg_sts, __BIT(irq % 32));
269}
270
271static void
272latte_establish_irq(struct pic_ops *pic, int irq, int type, int pri)
273{
274	unsigned reg_en = irq < 32 ? LT_IOPIRQINT1EN : LT_IOPIRQINT2EN;
275	unsigned reg_sts = irq < 32 ? LT_IOPINT1STS : LT_IOPINT2STS;
276	uint32_t val;
277
278	/* Mask and clear IOP interrupt */
279	val = RD4(reg_en);
280	val &= ~__BIT(irq % 32);
281	WR4(reg_en, val);
282	WR4(reg_sts, __BIT(irq % 32));
283}
284
285
286static void
287ahb_intr_init(int irq)
288{
289	struct pic_ops *pic_impl;
290	memset(pic_irqmask, 0, sizeof(pic_irqmask));
291
292	/* Mask and clear all interrupts. */
293	if (wiiu_native) {
294		WR4(LT_PPCnINT1EN(0), 0);
295		WR4(LT_PPCnINT2EN(0), 0);
296		WR4(LT_PPCnINT1STS(0), ~0U);
297		WR4(LT_PPCnINT2STS(0), ~0U);
298
299		pic_impl = &latte_pic;
300	} else {
301		WR4(HW_PPCIRQMASK, 0);
302		WR4(HW_PPCIRQFLAGS, ~0U);
303
304		pic_impl = &hollywood_pic;
305	}
306
307	pic_add(pic_impl);
308	intr_establish_xname(irq, IST_LEVEL, IPL_SCHED, pic_handle_intr,
309	    pic_impl, pic_impl->pic_name);
310}
311
312void *
313ahb_intr_establish(int irq, int ipl, int (*func)(void *), void *arg,
314    const char *name)
315{
316	if (wiiu_native) {
317		KASSERT(latte_pic.pic_intrbase != 0);
318
319		return intr_establish_xname(latte_pic.pic_intrbase + irq,
320		    IST_LEVEL, ipl, func, arg, name);
321	} else {
322		KASSERT(hollywood_pic.pic_intrbase != 0);
323
324		return intr_establish_xname(hollywood_pic.pic_intrbase + irq,
325		    IST_LEVEL, ipl, func, arg, name);
326	}
327}
328
329void
330ahb_claim_device(device_t dev, uint32_t mask)
331{
332	if (!wiiu_native) {
333		/*
334		 * Restrict IOP access to a device, giving exclusive access
335		 * to PPC.
336		 */
337		WR4(HW_AHBPROT, RD4(HW_AHBPROT) & ~mask);
338	}
339}
340