Home | History | Annotate | Line # | Download | only in dev
      1 /* $NetBSD: ahb.c,v 1.1 2026/01/09 22:54:29 jmcneill Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2024 Jared McNeill <jmcneill (at) 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 
     51 static struct mainbus_attach_args ahb_maa;
     52 static uint32_t pic_irqmask[2];
     53 
     54 static int	ahb_match(device_t, cfdata_t, void *);
     55 static void	ahb_attach(device_t, device_t, void *);
     56 
     57 static int	ahb_search(device_t, cfdata_t, const int *, void *);
     58 static int	ahb_print(void *, const char *);
     59 
     60 static void	ahb_intr_init(int);
     61 
     62 static void	hollywood_enable_irq(struct pic_ops *, int, int);
     63 static void	hollywood_disable_irq(struct pic_ops *, int);
     64 static int	hollywood_get_irq(struct pic_ops *, int);
     65 static void	hollywood_ack_irq(struct pic_ops *, int);
     66 static void	hollywood_establish_irq(struct pic_ops *, int, int, int);
     67 
     68 static 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 
     80 static void	latte_enable_irq(struct pic_ops *, int, int);
     81 static void	latte_disable_irq(struct pic_ops *, int);
     82 static int	latte_get_irq(struct pic_ops *, int);
     83 static void	latte_ack_irq(struct pic_ops *, int);
     84 static void	latte_establish_irq(struct pic_ops *, int, int, int);
     85 
     86 static 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 
     98 CFATTACH_DECL_NEW(ahb, 0, ahb_match, ahb_attach, NULL, NULL);
     99 
    100 static int
    101 ahb_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 
    108 static void
    109 ahb_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 
    135 static int
    136 ahb_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 
    156 static int
    157 ahb_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 
    175 static void
    176 hollywood_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 
    182 static void
    183 hollywood_disable_irq(struct pic_ops *pic, int irq)
    184 {
    185 	pic_irqmask[0] &= ~__BIT(irq);
    186 	WR4(HW_PPCIRQMASK, pic_irqmask[0]);
    187 }
    188 
    189 static int
    190 hollywood_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 
    205 static void
    206 hollywood_ack_irq(struct pic_ops *pic, int irq)
    207 {
    208 	WR4(HW_PPCIRQFLAGS, __BIT(irq));
    209 }
    210 
    211 static void
    212 hollywood_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 
    223 static void
    224 latte_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 
    232 static void
    233 latte_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 
    241 static int
    242 latte_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 
    263 static void
    264 latte_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 
    271 static void
    272 latte_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 
    286 static void
    287 ahb_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 
    312 void *
    313 ahb_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 
    329 void
    330 ahb_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