ahb.c revision 1.1
11.1Sjmcneill/* $NetBSD: ahb.c,v 1.1 2026/01/09 22:54:29 jmcneill Exp $ */
21.1Sjmcneill
31.1Sjmcneill/*-
41.1Sjmcneill * Copyright (c) 2024 Jared McNeill <jmcneill@invisible.ca>
51.1Sjmcneill * All rights reserved.
61.1Sjmcneill *
71.1Sjmcneill * Redistribution and use in source and binary forms, with or without
81.1Sjmcneill * modification, are permitted provided that the following conditions
91.1Sjmcneill * are met:
101.1Sjmcneill * 1. Redistributions of source code must retain the above copyright
111.1Sjmcneill *    notice, this list of conditions and the following disclaimer.
121.1Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright
131.1Sjmcneill *    notice, this list of conditions and the following disclaimer in the
141.1Sjmcneill *    documentation and/or other materials provided with the distribution.
151.1Sjmcneill *
161.1Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
171.1Sjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
181.1Sjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
191.1Sjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
201.1Sjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
211.1Sjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
221.1Sjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
231.1Sjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
241.1Sjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
251.1Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
261.1Sjmcneill * SUCH DAMAGE.
271.1Sjmcneill */
281.1Sjmcneill
291.1Sjmcneill#include <sys/cdefs.h>
301.1Sjmcneill__KERNEL_RCSID(0, "$NetBSD: ahb.c,v 1.1 2026/01/09 22:54:29 jmcneill Exp $");
311.1Sjmcneill
321.1Sjmcneill#include <sys/param.h>
331.1Sjmcneill#include <sys/bus.h>
341.1Sjmcneill#include <sys/device.h>
351.1Sjmcneill#include <sys/systm.h>
361.1Sjmcneill#include <sys/bitops.h>
371.1Sjmcneill
381.1Sjmcneill#include <machine/intr.h>
391.1Sjmcneill#include <machine/wii.h>
401.1Sjmcneill#include <machine/wiiu.h>
411.1Sjmcneill#include <machine/pio.h>
421.1Sjmcneill#include <powerpc/pic/picvar.h>
431.1Sjmcneill
441.1Sjmcneill#include "locators.h"
451.1Sjmcneill#include "mainbus.h"
461.1Sjmcneill#include "ahb.h"
471.1Sjmcneill
481.1Sjmcneill#define WR4(reg, val)		out32(reg, val)
491.1Sjmcneill#define RD4(reg)		in32(reg)
501.1Sjmcneill
511.1Sjmcneillstatic struct mainbus_attach_args ahb_maa;
521.1Sjmcneillstatic uint32_t pic_irqmask[2];
531.1Sjmcneill
541.1Sjmcneillstatic int	ahb_match(device_t, cfdata_t, void *);
551.1Sjmcneillstatic void	ahb_attach(device_t, device_t, void *);
561.1Sjmcneill
571.1Sjmcneillstatic int	ahb_search(device_t, cfdata_t, const int *, void *);
581.1Sjmcneillstatic int	ahb_print(void *, const char *);
591.1Sjmcneill
601.1Sjmcneillstatic void	ahb_intr_init(int);
611.1Sjmcneill
621.1Sjmcneillstatic void	hollywood_enable_irq(struct pic_ops *, int, int);
631.1Sjmcneillstatic void	hollywood_disable_irq(struct pic_ops *, int);
641.1Sjmcneillstatic int	hollywood_get_irq(struct pic_ops *, int);
651.1Sjmcneillstatic void	hollywood_ack_irq(struct pic_ops *, int);
661.1Sjmcneillstatic void	hollywood_establish_irq(struct pic_ops *, int, int, int);
671.1Sjmcneill
681.1Sjmcneillstatic struct pic_ops hollywood_pic = {
691.1Sjmcneill	.pic_name = "hollywood",
701.1Sjmcneill	.pic_numintrs = 32,
711.1Sjmcneill	.pic_cookie = NULL,
721.1Sjmcneill	.pic_enable_irq = hollywood_enable_irq,
731.1Sjmcneill	.pic_reenable_irq = hollywood_enable_irq,
741.1Sjmcneill	.pic_disable_irq = hollywood_disable_irq,
751.1Sjmcneill	.pic_get_irq = hollywood_get_irq,
761.1Sjmcneill	.pic_ack_irq = hollywood_ack_irq,
771.1Sjmcneill	.pic_establish_irq = hollywood_establish_irq,
781.1Sjmcneill};
791.1Sjmcneill
801.1Sjmcneillstatic void	latte_enable_irq(struct pic_ops *, int, int);
811.1Sjmcneillstatic void	latte_disable_irq(struct pic_ops *, int);
821.1Sjmcneillstatic int	latte_get_irq(struct pic_ops *, int);
831.1Sjmcneillstatic void	latte_ack_irq(struct pic_ops *, int);
841.1Sjmcneillstatic void	latte_establish_irq(struct pic_ops *, int, int, int);
851.1Sjmcneill
861.1Sjmcneillstatic struct pic_ops latte_pic = {
871.1Sjmcneill	.pic_name = "latte",
881.1Sjmcneill	.pic_numintrs = 64,
891.1Sjmcneill	.pic_cookie = NULL,
901.1Sjmcneill	.pic_enable_irq = latte_enable_irq,
911.1Sjmcneill	.pic_reenable_irq = latte_enable_irq,
921.1Sjmcneill	.pic_disable_irq = latte_disable_irq,
931.1Sjmcneill	.pic_get_irq = latte_get_irq,
941.1Sjmcneill	.pic_ack_irq = latte_ack_irq,
951.1Sjmcneill	.pic_establish_irq = latte_establish_irq,
961.1Sjmcneill};
971.1Sjmcneill
981.1SjmcneillCFATTACH_DECL_NEW(ahb, 0, ahb_match, ahb_attach, NULL, NULL);
991.1Sjmcneill
1001.1Sjmcneillstatic int
1011.1Sjmcneillahb_match(device_t parent, cfdata_t cf, void *aux)
1021.1Sjmcneill{
1031.1Sjmcneill	struct mainbus_attach_args *maa = aux;
1041.1Sjmcneill
1051.1Sjmcneill	return strcmp(maa->maa_name, "ahb") == 0;
1061.1Sjmcneill}
1071.1Sjmcneill
1081.1Sjmcneillstatic void
1091.1Sjmcneillahb_attach(device_t parent, device_t self, void *aux)
1101.1Sjmcneill{
1111.1Sjmcneill	uint32_t val;
1121.1Sjmcneill
1131.1Sjmcneill	ahb_maa = *(struct mainbus_attach_args *)aux;
1141.1Sjmcneill
1151.1Sjmcneill	aprint_naive("\n");
1161.1Sjmcneill	if (wiiu_native) {
1171.1Sjmcneill		val = RD4(LT_CHIPREVID);
1181.1Sjmcneill
1191.1Sjmcneill		aprint_normal(": Latte 0x%02x\n",
1201.1Sjmcneill		    (unsigned)(val & (LT_CHIPREVID_VERHI | LT_CHIPREVID_VERLO)));
1211.1Sjmcneill	} else {
1221.1Sjmcneill		val = RD4(HW_VERSION);
1231.1Sjmcneill
1241.1Sjmcneill		aprint_normal(": Hollywood ES%u.%u\n",
1251.1Sjmcneill		    (unsigned)__SHIFTOUT(val, HWVER_MASK) + 1,
1261.1Sjmcneill		    (unsigned)__SHIFTOUT(val, HWREV_MASK));
1271.1Sjmcneill	}
1281.1Sjmcneill
1291.1Sjmcneill	ahb_intr_init(ahb_maa.maa_irq);
1301.1Sjmcneill
1311.1Sjmcneill	config_search(self, NULL,
1321.1Sjmcneill	    CFARGS(.search = ahb_search));
1331.1Sjmcneill}
1341.1Sjmcneill
1351.1Sjmcneillstatic int
1361.1Sjmcneillahb_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
1371.1Sjmcneill{
1381.1Sjmcneill	struct ahb_attach_args aaa;
1391.1Sjmcneill
1401.1Sjmcneill	aaa.aaa_bst = ahb_maa.maa_bst;
1411.1Sjmcneill	aaa.aaa_dmat = ahb_maa.maa_dmat;
1421.1Sjmcneill	if (cf->cf_loc[AHBCF_ADDR] != AHBCF_ADDR_DEFAULT) {
1431.1Sjmcneill		aaa.aaa_addr = cf->cf_loc[AHBCF_ADDR];
1441.1Sjmcneill	} else {
1451.1Sjmcneill		aaa.aaa_addr = 0;
1461.1Sjmcneill	}
1471.1Sjmcneill	aaa.aaa_irq = cf->cf_loc[AHBCF_IRQ];
1481.1Sjmcneill
1491.1Sjmcneill	if (config_probe(parent, cf, &aaa)) {
1501.1Sjmcneill		config_attach(parent, cf, &aaa, ahb_print, CFARGS_NONE);
1511.1Sjmcneill	}
1521.1Sjmcneill
1531.1Sjmcneill	return 0;
1541.1Sjmcneill}
1551.1Sjmcneill
1561.1Sjmcneillstatic int
1571.1Sjmcneillahb_print(void *aux, const char *pnp)
1581.1Sjmcneill{
1591.1Sjmcneill	struct ahb_attach_args *aaa = aux;
1601.1Sjmcneill
1611.1Sjmcneill	if (pnp != NULL) {
1621.1Sjmcneill		return QUIET;
1631.1Sjmcneill	}
1641.1Sjmcneill
1651.1Sjmcneill	if (aaa->aaa_addr != 0) {
1661.1Sjmcneill		aprint_normal(" addr 0x%08lx", aaa->aaa_addr);
1671.1Sjmcneill	}
1681.1Sjmcneill	if (aaa->aaa_irq != AHBCF_IRQ_DEFAULT) {
1691.1Sjmcneill		aprint_normal(" irq %d", aaa->aaa_irq);
1701.1Sjmcneill	}
1711.1Sjmcneill
1721.1Sjmcneill	return UNCONF;
1731.1Sjmcneill}
1741.1Sjmcneill
1751.1Sjmcneillstatic void
1761.1Sjmcneillhollywood_enable_irq(struct pic_ops *pic, int irq, int type)
1771.1Sjmcneill{
1781.1Sjmcneill	pic_irqmask[0] |= __BIT(irq);
1791.1Sjmcneill	WR4(HW_PPCIRQMASK, pic_irqmask[0]);
1801.1Sjmcneill}
1811.1Sjmcneill
1821.1Sjmcneillstatic void
1831.1Sjmcneillhollywood_disable_irq(struct pic_ops *pic, int irq)
1841.1Sjmcneill{
1851.1Sjmcneill	pic_irqmask[0] &= ~__BIT(irq);
1861.1Sjmcneill	WR4(HW_PPCIRQMASK, pic_irqmask[0]);
1871.1Sjmcneill}
1881.1Sjmcneill
1891.1Sjmcneillstatic int
1901.1Sjmcneillhollywood_get_irq(struct pic_ops *pic, int mode)
1911.1Sjmcneill{
1921.1Sjmcneill	uint32_t raw, pend;
1931.1Sjmcneill	int irq;
1941.1Sjmcneill
1951.1Sjmcneill	raw = RD4(HW_PPCIRQFLAGS);
1961.1Sjmcneill	pend = raw & pic_irqmask[0];
1971.1Sjmcneill	if (pend == 0) {
1981.1Sjmcneill		return 255;
1991.1Sjmcneill	}
2001.1Sjmcneill	irq = ffs32(pend) - 1;
2011.1Sjmcneill
2021.1Sjmcneill	return irq;
2031.1Sjmcneill}
2041.1Sjmcneill
2051.1Sjmcneillstatic void
2061.1Sjmcneillhollywood_ack_irq(struct pic_ops *pic, int irq)
2071.1Sjmcneill{
2081.1Sjmcneill	WR4(HW_PPCIRQFLAGS, __BIT(irq));
2091.1Sjmcneill}
2101.1Sjmcneill
2111.1Sjmcneillstatic void
2121.1Sjmcneillhollywood_establish_irq(struct pic_ops *pic, int irq, int type, int pri)
2131.1Sjmcneill{
2141.1Sjmcneill	uint32_t val;
2151.1Sjmcneill
2161.1Sjmcneill	/* Mask and clear Starlet interrupt */
2171.1Sjmcneill	val = RD4(HW_ARMIRQMASK);
2181.1Sjmcneill	val &= ~__BIT(irq);
2191.1Sjmcneill	WR4(HW_ARMIRQMASK, val);
2201.1Sjmcneill	WR4(HW_ARMIRQFLAGS, __BIT(irq));
2211.1Sjmcneill}
2221.1Sjmcneill
2231.1Sjmcneillstatic void
2241.1Sjmcneilllatte_enable_irq(struct pic_ops *pic, int irq, int type)
2251.1Sjmcneill{
2261.1Sjmcneill	unsigned reg_en = irq < 32 ? LT_PPCnINT1EN(0) : LT_PPCnINT2EN(0);
2271.1Sjmcneill
2281.1Sjmcneill	pic_irqmask[irq / 32] |= __BIT(irq % 32);
2291.1Sjmcneill	WR4(reg_en, pic_irqmask[irq / 32]);
2301.1Sjmcneill}
2311.1Sjmcneill
2321.1Sjmcneillstatic void
2331.1Sjmcneilllatte_disable_irq(struct pic_ops *pic, int irq)
2341.1Sjmcneill{
2351.1Sjmcneill	unsigned reg_en = irq < 32 ? LT_PPCnINT1EN(0) : LT_PPCnINT2EN(0);
2361.1Sjmcneill
2371.1Sjmcneill	pic_irqmask[irq / 32] &= ~__BIT(irq % 32);
2381.1Sjmcneill	WR4(reg_en, pic_irqmask[irq / 32]);
2391.1Sjmcneill}
2401.1Sjmcneill
2411.1Sjmcneillstatic int
2421.1Sjmcneilllatte_get_irq(struct pic_ops *pic, int mode)
2431.1Sjmcneill{
2441.1Sjmcneill	uint32_t raw, pend;
2451.1Sjmcneill	int irq;
2461.1Sjmcneill
2471.1Sjmcneill	raw = RD4(LT_PPCnINT1STS(0));
2481.1Sjmcneill	pend = raw & pic_irqmask[0];
2491.1Sjmcneill	if (pend == 0) {
2501.1Sjmcneill		raw = RD4(LT_PPCnINT2STS(0));
2511.1Sjmcneill		pend = raw & pic_irqmask[1];
2521.1Sjmcneill		if (pend == 0) {
2531.1Sjmcneill			return 255;
2541.1Sjmcneill		}
2551.1Sjmcneill		irq = 32 + ffs32(pend) - 1;
2561.1Sjmcneill	} else {
2571.1Sjmcneill		irq = ffs32(pend) - 1;
2581.1Sjmcneill	}
2591.1Sjmcneill
2601.1Sjmcneill	return irq;
2611.1Sjmcneill}
2621.1Sjmcneill
2631.1Sjmcneillstatic void
2641.1Sjmcneilllatte_ack_irq(struct pic_ops *pic, int irq)
2651.1Sjmcneill{
2661.1Sjmcneill	unsigned reg_sts = irq < 32 ? LT_PPCnINT1STS(0) : LT_PPCnINT2STS(0);
2671.1Sjmcneill
2681.1Sjmcneill	WR4(reg_sts, __BIT(irq % 32));
2691.1Sjmcneill}
2701.1Sjmcneill
2711.1Sjmcneillstatic void
2721.1Sjmcneilllatte_establish_irq(struct pic_ops *pic, int irq, int type, int pri)
2731.1Sjmcneill{
2741.1Sjmcneill	unsigned reg_en = irq < 32 ? LT_IOPIRQINT1EN : LT_IOPIRQINT2EN;
2751.1Sjmcneill	unsigned reg_sts = irq < 32 ? LT_IOPINT1STS : LT_IOPINT2STS;
2761.1Sjmcneill	uint32_t val;
2771.1Sjmcneill
2781.1Sjmcneill	/* Mask and clear IOP interrupt */
2791.1Sjmcneill	val = RD4(reg_en);
2801.1Sjmcneill	val &= ~__BIT(irq % 32);
2811.1Sjmcneill	WR4(reg_en, val);
2821.1Sjmcneill	WR4(reg_sts, __BIT(irq % 32));
2831.1Sjmcneill}
2841.1Sjmcneill
2851.1Sjmcneill
2861.1Sjmcneillstatic void
2871.1Sjmcneillahb_intr_init(int irq)
2881.1Sjmcneill{
2891.1Sjmcneill	struct pic_ops *pic_impl;
2901.1Sjmcneill	memset(pic_irqmask, 0, sizeof(pic_irqmask));
2911.1Sjmcneill
2921.1Sjmcneill	/* Mask and clear all interrupts. */
2931.1Sjmcneill	if (wiiu_native) {
2941.1Sjmcneill		WR4(LT_PPCnINT1EN(0), 0);
2951.1Sjmcneill		WR4(LT_PPCnINT2EN(0), 0);
2961.1Sjmcneill		WR4(LT_PPCnINT1STS(0), ~0U);
2971.1Sjmcneill		WR4(LT_PPCnINT2STS(0), ~0U);
2981.1Sjmcneill
2991.1Sjmcneill		pic_impl = &latte_pic;
3001.1Sjmcneill	} else {
3011.1Sjmcneill		WR4(HW_PPCIRQMASK, 0);
3021.1Sjmcneill		WR4(HW_PPCIRQFLAGS, ~0U);
3031.1Sjmcneill
3041.1Sjmcneill		pic_impl = &hollywood_pic;
3051.1Sjmcneill	}
3061.1Sjmcneill
3071.1Sjmcneill	pic_add(pic_impl);
3081.1Sjmcneill	intr_establish_xname(irq, IST_LEVEL, IPL_SCHED, pic_handle_intr,
3091.1Sjmcneill	    pic_impl, pic_impl->pic_name);
3101.1Sjmcneill}
3111.1Sjmcneill
3121.1Sjmcneillvoid *
3131.1Sjmcneillahb_intr_establish(int irq, int ipl, int (*func)(void *), void *arg,
3141.1Sjmcneill    const char *name)
3151.1Sjmcneill{
3161.1Sjmcneill	if (wiiu_native) {
3171.1Sjmcneill		KASSERT(latte_pic.pic_intrbase != 0);
3181.1Sjmcneill
3191.1Sjmcneill		return intr_establish_xname(latte_pic.pic_intrbase + irq,
3201.1Sjmcneill		    IST_LEVEL, ipl, func, arg, name);
3211.1Sjmcneill	} else {
3221.1Sjmcneill		KASSERT(hollywood_pic.pic_intrbase != 0);
3231.1Sjmcneill
3241.1Sjmcneill		return intr_establish_xname(hollywood_pic.pic_intrbase + irq,
3251.1Sjmcneill		    IST_LEVEL, ipl, func, arg, name);
3261.1Sjmcneill	}
3271.1Sjmcneill}
3281.1Sjmcneill
3291.1Sjmcneillvoid
3301.1Sjmcneillahb_claim_device(device_t dev, uint32_t mask)
3311.1Sjmcneill{
3321.1Sjmcneill	if (!wiiu_native) {
3331.1Sjmcneill		/*
3341.1Sjmcneill		 * Restrict IOP access to a device, giving exclusive access
3351.1Sjmcneill		 * to PPC.
3361.1Sjmcneill		 */
3371.1Sjmcneill		WR4(HW_AHBPROT, RD4(HW_AHBPROT) & ~mask);
3381.1Sjmcneill	}
3391.1Sjmcneill}
340