root/sys/arch/ofppc/ofppc/mainbus.c
/*      $NetBSD: mainbus.c,v 1.33 2026/06/30 22:28:00 rkujawa Exp $     */

/*-
 * Copyright (c) 2007 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Tim Rightnour
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: mainbus.c,v 1.33 2026/06/30 22:28:00 rkujawa Exp $");

#include "opt_interrupt.h"
#include "opt_multiprocessor.h"

#include <sys/param.h>
#include <sys/device.h>
#include <sys/systm.h>

#include <dev/pci/pcivar.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_pci.h>
#include <arch/powerpc/pic/picvar.h>
#ifdef MULTIPROCESSOR
#include <arch/powerpc/pic/ipivar.h>
#endif
#include <machine/pci_machdep.h>
#include <machine/autoconf.h>

#include <dev/isa/isareg.h>
#include <dev/isa/isavar.h>

int     mainbus_match(device_t, cfdata_t, void *);
void    mainbus_attach(device_t, device_t, void *);

CFATTACH_DECL_NEW(mainbus, 0,
    mainbus_match, mainbus_attach, NULL, NULL);

int mainbus_found = 0;
struct pic_ops *isa_pic;

#ifdef PIC_PREPIVR
vaddr_t prep_intr_reg;
uint32_t prep_intr_reg_off;
#endif

extern ofw_pic_node_t picnodes[8];
extern int nrofpics;
extern int primary_pic;

#ifdef PIC_PREPIVR
static struct pic_ops *
init_prepivr(int node)
{
        int pcinode;
        uint32_t ivr;

        pcinode = OF_finddevice("/pci");
        if (OF_getprop(pcinode, "8259-interrupt-acknowledge", &ivr,
                sizeof(ivr)) != sizeof(ivr)) {
                aprint_error("Incorrectly identified i8259 as prepivr\n");
                return setup_i8259();
        }
        prep_intr_reg = (vaddr_t)mapiodev(ivr, sizeof(uint32_t), false);
        prep_intr_reg_off = 0; /* hack */
        if (!prep_intr_reg)
                panic("startup: no room for interrupt register");

        return setup_prepivr(PIC_IVR_MOT);
}
#endif

static int
init_openpic(int node)
{
        struct ofw_pci_register aadr;
        struct ranges {
                uint32_t pci_hi, pci_mid, pci_lo;
                uint32_t host;
                uint32_t size_hi, size_lo;
        } ranges[6];
        uint32_t reg[12];
        int parent, len;
#if defined(PIC_OPENPIC) || defined(PIC_DISTOPENPIC)
        unsigned char *baseaddr;
#endif
#ifdef PIC_OPENPIC
        struct ranges *rp = ranges;
#endif
#ifdef PIC_DISTOPENPIC
        unsigned char *isu[OPENPIC_MAX_ISUS];
        int i, j;
        int isumap[OPENPIC_MAX_ISUS];
#endif

        if (OF_getprop(node, "assigned-addresses", &aadr, sizeof(aadr))
            != sizeof(aadr))
                goto noaadr;

        parent = OF_parent(node);
        len = OF_getprop(parent, "ranges", ranges, sizeof(ranges));
        if (len == -1)
                goto noaadr;

#ifdef PIC_OPENPIC
        while (len >= sizeof(ranges[0])) {
                if ((rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) ==
                    (aadr.phys_hi & OFW_PCI_PHYS_HI_SPACEMASK) &&
                    (aadr.size_lo + aadr.phys_lo <= (rp->size_lo+rp->host))) {
                        baseaddr = (unsigned char *)mapiodev(
                            rp->host | aadr.phys_lo, aadr.size_lo, false);
                        aprint_normal("Found openpic at %08x\n",
                            rp->host | aadr.phys_lo);
                        setup_openpic(baseaddr, 0);
#ifdef MULTIPROCESSOR
                        setup_openpic_ipi();
                        ipiops.ppc_establish_ipi(IST_LEVEL, IPL_HIGH, NULL);
                        for (i=1; i < ncpu; i++) {
                                aprint_verbose("Enabling interrupts "
                                    "for cpu%d\n", i);
                                openpic_set_priority(i, 0);
                        }
#endif
                        return TRUE;
                }
                rp++;
                len -= sizeof(ranges[0]);
        }
#endif
        return FALSE;
 noaadr:
        /* this isn't a PCI-attached openpic */
        len = OF_getprop(node, "reg", &reg, sizeof(reg));
        if (len < sizeof(int)*2)
                return FALSE;

        if (len == sizeof(int)*2) {
                aprint_verbose("Found openpic at %08x\n", reg[0]);
#ifdef PIC_OPENPIC
                baseaddr = (unsigned char *)mapiodev(reg[0], reg[1], false);
                (void)setup_openpic(baseaddr, 0);
#ifdef MULTIPROCESSOR
                setup_openpic_ipi();
                ipiops.ppc_establish_ipi(IST_LEVEL, IPL_HIGH, NULL);
                for (i=1; i < ncpu; i++) {
                        aprint_verbose("Enabling interrupts for cpu%d\n", i);
                        openpic_set_priority(i, 0);
                }
#endif
                return TRUE;
#else
                aprint_error("No openpic support compiled into kernel!");
                return FALSE;
#endif
        }

#ifdef PIC_DISTOPENPIC
        /* otherwise, we have a distributed openpic */
        i = len/(sizeof(int)*2) - 1;
        if (i == 0)
                return FALSE;
        if (i > OPENPIC_MAX_ISUS)
                aprint_error("Increase OPENPIC_MAX_ISUS to %d\n", i);

        baseaddr = (unsigned char *)mapiodev(reg[0], 0x40000, false);
        aprint_verbose("Found openpic at %08x\n", reg[0]);

        for (j=0; j < i; j++) {
                isu[j] = (unsigned char *)mapiodev(reg[(j+1)*2],
                    reg[(j+1)*2+1], false);
                isumap[j] = reg[(j+1)*2+1];
        }
        (void)setup_distributed_openpic(baseaddr, i, (void **)isu, isumap);
#ifdef MULTIPROCESSOR
        setup_openpic_ipi();
        ipiops.ppc_establish_ipi(IST_LEVEL, IPL_HIGH, NULL);
        for (i=1; i < ncpu; i++) {
                aprint_verbose("Enabling interrupts for cpu%d\n", i);
                openpic_set_priority(i, 0);
        }
#endif
        return TRUE;
#endif
        aprint_error("PIC support not present or PIC error\n");
        return FALSE;
}


/*
 * Probe for the mainbus; always succeeds.
 */
int
mainbus_match(device_t parent, cfdata_t cf, void *aux)
{
        if (mainbus_found)
                return 0;
        return 1;
}

/*
 * Attach the mainbus.
 */
void
mainbus_attach(device_t parent, device_t self, void *aux)
{
        struct ofbus_attach_args oba;
        struct confargs ca;
        int node, rtnode, i;
        u_int32_t reg[4];
        char name[32];

        mainbus_found = 1;

        aprint_normal("\n");

        /* Find rtas first */
        rtnode = OF_finddevice("/rtas");
        if (rtnode != -1) {
                memset(name, 0, sizeof(name));
                if (OF_getprop(rtnode, "name", name, sizeof(name)) != -1) {
                        ca.ca_name = name;
                        ca.ca_node = rtnode;
                        ca.ca_nreg = OF_getprop(rtnode, "reg", reg,
                            sizeof(reg));
                        ca.ca_reg  = reg;
                        config_found(self, &ca, NULL, CFARGS_NONE);
                }
        }

        /* Now find CPU's */
        for (i = 0; i < CPU_MAXNUM; i++) {
                ca.ca_name = "cpu";
                ca.ca_reg = reg;
                reg[0] = i;
                config_found(self, &ca, NULL, CFARGS_NONE);
        }

        node = OF_peer(0);
        if (node) {
                oba.oba_busname = "ofw";
                oba.oba_phandle = node;
                config_found(self, &oba, NULL, CFARGS_NONE);
        }

        if (strcmp(model_name, "Pegasos2") == 0) {
                /*
                 * Configure to System Controller MV64361.
                 * And skip other devices.  These attached from it.
                 */
                ca.ca_name = "gt";

                config_found(self, &ca, NULL, CFARGS_NONE);

                goto config_fin;
        }

        /* this primarily searches for pci bridges on the root bus */
        for (node = OF_child(OF_finddevice("/")); node; node = OF_peer(node)) {
                memset(name, 0, sizeof(name));
                if (OF_getprop(node, "name", name, sizeof(name)) == -1)
                        continue;
                /* skip rtas */
                if (node == rtnode)
                        continue;

                ca.ca_name = name;
                ca.ca_node = node;
                ca.ca_nreg = OF_getprop(node, "reg", reg, sizeof(reg));
                ca.ca_reg  = reg;

                config_found(self, &ca, NULL, CFARGS_NONE);
        }

config_fin:
        pic_finish_setup();
}

void
init_interrupt(void)
{
        /* Do nothing, not ready yet */
}

void
init_ofppc_interrupt(void)
{
        int node, i, isa_cascade = 0;

        /* Now setup the PIC's */
        node = OF_finddevice("/");
        if (node <= 0)
                panic("Can't find root OFW device node\n");
        genofw_find_ofpics(node);
        genofw_fixup_picnode_offsets();
        pic_init();

        /* find ISA first */
        for (i = 0; i < nrofpics; i++) {
                if (picnodes[i].type == PICNODE_TYPE_8259) {
                        aprint_debug("calling i8259 setup\n");
#ifdef PIC_I8259
                        isa_pic = setup_i8259();
#endif
                }
                if (picnodes[i].type == PICNODE_TYPE_IVR) {
                        aprint_debug("calling prepivr setup\n");
#ifdef PIC_PREPIVR
                        isa_pic = init_prepivr(picnodes[i].node);
#elif defined(PIC_I8259)
                        isa_pic = setup_i8259();
#endif
                }
        }
        for (i = 0; i < nrofpics; i++) {
                if (picnodes[i].type == PICNODE_TYPE_8259)
                        continue;
                if (picnodes[i].type == PICNODE_TYPE_IVR)
                        continue;
                if (picnodes[i].type == PICNODE_TYPE_OPENPIC) {
                        aprint_debug("calling openpic setup\n");
                        if (isa_pic != NULL)
                                isa_cascade = 1;
                        (void)init_openpic(picnodes[i].node);
                } else if (picnodes[i].type == PICNODE_TYPE_MPC5200) {
                        aprint_debug("mpc5200 SIU pic set up as a device\n");
                } else
                        aprint_error("Unhandled pic node type node=%x\n",
                            picnodes[i].node);
        }
        if (isa_cascade) {
                primary_pic = 1;
                intr_establish(16, IST_LEVEL, IPL_HIGH, pic_handle_intr,
                    isa_pic);
        }
}