root/sys/arch/prep/prep/machdep.c
/*      $NetBSD: machdep.c,v 1.81 2025/12/21 07:00:28 skrll Exp $       */

/*
 * Copyright (C) 1995, 1996 Wolfgang Solfrank.
 * Copyright (C) 1995, 1996 TooLs GmbH.
 * All rights reserved.
 *
 * 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.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by TooLs GmbH.
 * 4. The name of TooLs GmbH may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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: machdep.c,v 1.81 2025/12/21 07:00:28 skrll Exp $");

#include "opt_compat_netbsd.h"
#include "opt_openpic.h"

#include <sys/param.h>
#include <sys/buf.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/exec.h>
#include <sys/extent.h>
#include <sys/intr.h>
#include <sys/kernel.h>
#include <sys/mbuf.h>
#include <sys/mount.h>
#include <sys/msgbuf.h>
#include <sys/proc.h>
#include <sys/reboot.h>
#include <sys/syscallargs.h>
#include <sys/syslog.h>
#include <sys/systm.h>
#include <sys/sysctl.h>

#include <uvm/uvm_extern.h>

#include <machine/autoconf.h>
#include <machine/bootinfo.h>
#include <machine/platform.h>
#include <machine/powerpc.h>
#include <machine/residual.h>

#include <powerpc/pmap.h>
#include <powerpc/trap.h>

#include <powerpc/oea/bat.h>
#include <powerpc/openpic.h>
#include <powerpc/pic/picvar.h>
#ifdef MULTIPROCESSOR
#include <powerpc/pic/ipivar.h>
#endif

#include <dev/cons.h>

#include "com.h"
#if (NCOM > 0)
#include <sys/termios.h>
#include <dev/ic/comreg.h>
#include <dev/ic/comvar.h>
#endif

#include "opt_interrupt.h"

void initppc(u_long, u_long, u_int, void *);
void dumpsys(void);
static void prep_init(void);
static void init_intr(void);

char bootinfo[BOOTINFO_MAXSIZE];
char bootpath[256];

vaddr_t prep_intr_reg;                  /* PReP interrupt vector register */
uint32_t prep_intr_reg_off;             /* IVR offset within the mapped page */

#define OFMEMREGIONS    32
struct mem_region physmemr[OFMEMREGIONS], availmemr[OFMEMREGIONS];

paddr_t avail_end;                      /* XXX temporary */
struct pic_ops *isa_pic;
int isa_pcmciamask = 0x8b28;
uint32_t busfreq;

extern int primary_pic;
extern struct platform_quirkdata platform_quirks[];

RESIDUAL *res;
RESIDUAL resdata;

void
initppc(u_long startkernel, u_long endkernel, u_int args, void *btinfo)
{

        /*
         * copy bootinfo
         */
        memcpy(bootinfo, btinfo, sizeof(bootinfo));

        /*
         * copy residual data
         */
        {
                struct btinfo_residual *resinfo;

                resinfo =
                    (struct btinfo_residual *)lookup_bootinfo(BTINFO_RESIDUAL);
                if (!resinfo)
                        panic("not found residual information in bootinfo");

                if (((RESIDUAL *)resinfo->addr != 0) &&
                    ((RESIDUAL *)resinfo->addr)->ResidualLength != 0) {
                        memcpy(&resdata, resinfo->addr, sizeof(resdata));
                        res = &resdata;
                } else
                        panic("No residual data.");
        }
        aprint_normal("got residual data\n");

        /*
         * Set memory region
         */
        {
                u_long memsize = res->TotalMemory;

                physmemr[0].start = 0;
                physmemr[0].size = memsize & ~PGOFSET;
                availmemr[0].start = (endkernel + PGOFSET) & ~PGOFSET;
                availmemr[0].size = memsize - availmemr[0].start;
        }
        avail_end = physmemr[0].start + physmemr[0].size;    /* XXX temporary */

        /*
         * Set CPU clock
         */
        {
                struct btinfo_clock *clockinfo;
                extern u_long ticks_per_sec, ns_per_tick;
                VPD *vpd;

                clockinfo =
                    (struct btinfo_clock *)lookup_bootinfo(BTINFO_CLOCK);
                if (!clockinfo)
                        panic("not found clock information in bootinfo");

                ticks_per_sec = clockinfo->ticks_per_sec;
                ns_per_tick = 1000000000 / ticks_per_sec;

                vpd = &res->VitalProductData;
                busfreq = be32toh(vpd->ProcessorBusHz);
        }

        prep_initppc(startkernel, endkernel, args,
            /* rs6k-style PCI bridge */
            0xbf800000, BAT_BL_8M,              /* XXX magic number */
            0);
}

/*
 * Machine dependent startup code.
 */
void
cpu_startup(void)
{
        /*
         * Do common startup.
         */
        oea_startup(res->VitalProductData.PrintableModel);

        /*
         * General prep setup using pnp residual. Also provides for
         * external interrupt handler install
         */
        prep_init();

        /*
         * Now allow hardware interrupts.
         */
        {
                int msr;

                splraise(-1);
                __asm volatile ("mfmsr %0; ori %0,%0,%1; mtmsr %0"
                              : "=r"(msr) : "K"(PSL_EE));
        }
        /*
         * Now safe for bus space allocation to use malloc.
         */
        bus_space_mallocok();
}

/*
 * lookup_bootinfo:
 * Look up information in bootinfo of boot loader.
 */
void *
lookup_bootinfo(int type)
{
        struct btinfo_common *bt;
        struct btinfo_common *help = (struct btinfo_common *)bootinfo;

        do {
                bt = help;
                if (bt->type == type)
                        return (help);
                help = (struct btinfo_common *)((char*)help + bt->next);
        } while (bt->next &&
                (size_t)help < (size_t)bootinfo + sizeof (bootinfo));

        return (NULL);
}

/*
 * Halt or reboot the machine after syncing/dumping according to howto.
 */
void
cpu_reboot(int howto, char *what)
{
        static int syncing;

        if (cold) {
                howto |= RB_HALT;
                goto halt_sys;
        }

        boothowto = howto;
        if ((howto & RB_NOSYNC) == 0 && syncing == 0) {
                syncing = 1;
                vfs_shutdown();         /* sync */
        }

        /* Disable intr */
        splhigh();

#ifdef MULTIPROCESSOR
        /* Halt other CPUs */
        ppc_send_ipi(IPI_T_NOTME, PPC_IPI_HALT);
        delay(100000);  /* XXX */
#endif

        /* Do dump if requested */
        if ((howto & (RB_DUMP | RB_HALT)) == RB_DUMP)
                oea_dumpsys();

halt_sys:
        doshutdownhooks();

        pmf_system_shutdown(boothowto);

        if (howto & RB_HALT) {
                printf("\n");
                printf("The operating system has halted.\n");
                printf("Please press any key to reboot.\n\n");
                cnpollc(true);  /* for proper keyboard command handling */
                cngetc();
                cnpollc(false);
        }

        printf("rebooting...\n\n");

        reset_prep();

        for (;;)
                continue;
        /* NOTREACHED */
}

struct powerpc_bus_space prep_eisa_io_space_tag = {
        .pbs_flags = _BUS_SPACE_LITTLE_ENDIAN|_BUS_SPACE_IO_TYPE,
        .pbs_offset = 0x80000000,
        .pbs_base = 0x00000000,
        .pbs_limit = 0x0000f000,
};

struct powerpc_bus_space prep_eisa_mem_space_tag = {
        .pbs_flags = _BUS_SPACE_LITTLE_ENDIAN|_BUS_SPACE_MEM_TYPE,
        .pbs_offset = 0xC0000000,
        .pbs_base = 0x00000000,
        .pbs_limit = 0x3f000000,
};

#if defined(PIC_OPENPIC)

static int
prep_setup_openpic(PPC_DEVICE *dev)
{
        uint32_t l;
        uint8_t *p;
        int tag, size, item, i;
        unsigned char *baseaddr = NULL;

        l = be32toh(dev->AllocatedOffset);
        p = res->DevicePnPHeap + l;

        i = find_platform_quirk(res->VitalProductData.PrintableModel);

        /* look for the large vendor item that describes the MPIC's memory
         * range */
        for (; p[0] != END_TAG; p += size) {
                struct _L4_Pack *pack = (void *)p;
                struct _L4_PPCPack *pa = &pack->L4_Data.L4_PPCPack;

                tag = *p;
                if (tag_type(p[0]) == PNP_SMALL) {
                        size = tag_small_count(tag) + 1;
                        continue;
                }
                size = (p[1] | (p[2] << 8)) + 3 /* tag + length */;
                item = tag_large_item_name(tag);
                if (item != LargeVendorItem || pa->Type != LV_GenericAddress)
                        continue;
                /* otherwise, we have a memory packet */
                if (pa->PPCData[0] == 1)
                        baseaddr = (unsigned char *)mapiodev(
                            le64dec(&pa->PPCData[4]) | PREP_BUS_SPACE_IO,
                            le64dec(&pa->PPCData[12]), false);
                else if (pa->PPCData[0] == 2)
                        baseaddr = (unsigned char *)mapiodev(
                            le64dec(&pa->PPCData[4]) | PREP_BUS_SPACE_MEM,
                            le64dec(&pa->PPCData[12]), false);
                if (baseaddr == NULL)
                        return 0;
                pic_init();
                if (i != -1 &&
                    (platform_quirks[i].quirk & PLAT_QUIRK_ISA_HANDLER &&
                     platform_quirks[i].isa_intr_handler == EXT_INTR_I8259)) {
                        isa_pic = setup_prepivr(PIC_IVR_MOT);
                } else
                        isa_pic = setup_prepivr(PIC_IVR_IBM);
                (void)setup_openpic(baseaddr, 0);
                /* set the timebase frequency to 1/8th busfreq */
                openpic_write(OPENPIC_TIMER_FREQ, busfreq/8);
                primary_pic = 1;
                /* set up the IVR as a cascade on openpic 0 */
                intr_establish(16, IST_LEVEL, IPL_HIGH, pic_handle_intr,
                    isa_pic);
                oea_install_extint(pic_ext_intr);
#ifdef MULTIPROCESSOR
                setup_openpic_ipi();
#endif
                return 1;
        }
        return 0;
}

#endif /* PIC_OPENPIC */

/*
 * Locate and setup the isa_ivr.
 */

static void
setup_ivr(PPC_DEVICE *dev)
{
        uint32_t l, addr;
        uint8_t *p;
        int tag, size, item;

        l = be32toh(dev->AllocatedOffset);
        p = res->DevicePnPHeap + l;

        /* Find the IVR vector's Generic Address in a LVI */
        for (; p[0] != END_TAG; p += size) {
                struct _L4_Pack *pack = (void *)p;
                struct _L4_PPCPack *pa = &pack->L4_Data.L4_PPCPack;

                tag = *p;
                if (tag_type(p[0]) == PNP_SMALL) {
                        size = tag_small_count(tag) + 1;
                        continue;
                }
                size = (p[1] | (p[2] << 8)) + 3 /* tag + length */;
                item = tag_large_item_name(tag);
                if (item != LargeVendorItem || pa->Type != LV_GenericAddress)
                        continue;
                /* otherwise we have a memory packet */
                addr = le64dec(&pa->PPCData[4]) & ~(PAGE_SIZE-1);
                prep_intr_reg_off = le64dec(&pa->PPCData[4]) & (PAGE_SIZE-1);
                prep_intr_reg = (vaddr_t)mapiodev(addr, PAGE_SIZE, false);
                if (!prep_intr_reg)
                        panic("startup: no room for interrupt register");
                return;
        }
}

/*
 * There are a few things that need setting up early on in the prep
 * architecture.  Foremost of these is the MPIC (if present) and the
 * l2 cache controller.  This is a cut-down version of pnpbus_search()
 * that looks for specific devices, and sets them up accordingly.
 * This should also look for and wire up the interrupt vector.
 */

static void
prep_init(void)
{
        PPC_DEVICE *ppc_dev;
        int i, foundmpic;
        uint32_t ndev;

        ndev = be32toh(res->ActualNumDevices);
        ppc_dev = res->Devices;
        foundmpic = 0;
        prep_intr_reg = 0;

        for (i = 0; i < ((ndev > MAX_DEVICES) ? MAX_DEVICES : ndev); i++) {
                if (ppc_dev[i].DeviceId.DevId == 0x41d00000) /* ISA_PIC */
                        setup_ivr(&ppc_dev[i]);
#if defined(PIC_OPENPIC)
                if (ppc_dev[i].DeviceId.DevId == 0x244d000d) { /* MPIC */
                        foundmpic = prep_setup_openpic(&ppc_dev[i]);
                }
#endif

        }
        if (!prep_intr_reg) {
                /*
                 * For some reason we never found one, this is known to
                 * occur on certain motorola VME boards.  Instead we need
                 * to just hardcode it.
                 */
                prep_intr_reg = (vaddr_t) mapiodev(PREP_INTR_REG, PAGE_SIZE, false);
                if (!prep_intr_reg)
                        panic("startup: no room for interrupt register");
                prep_intr_reg_off = INTR_VECTOR_REG;
        }
        if (!foundmpic)
                init_intr();
}

static void
init_intr(void)
{
        int i;

#if defined(PIC_OPENPIC)
        openpic_base = 0;
#endif

        pic_init();
        i = find_platform_quirk(res->VitalProductData.PrintableModel);
        if (i != -1)
                if (platform_quirks[i].quirk & PLAT_QUIRK_ISA_HANDLER &&
                    platform_quirks[i].isa_intr_handler == EXT_INTR_I8259) {
                        isa_pic = setup_prepivr(PIC_IVR_MOT);
                        return;
                }
        isa_pic = setup_prepivr(PIC_IVR_IBM);
        oea_install_extint(pic_ext_intr);
}