root/sys/arch/vax/vax/ka88.c
/*      $NetBSD: ka88.c,v 1.22 2023/12/20 15:34:45 thorpej Exp $        */

/*
 * Copyright (c) 2000 Ludd, University of Lule}, Sweden. 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
 */

/*
 * KA88 specific CPU code.
 */
/*
 * TODO:
 *      - Machine check code
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ka88.c,v 1.22 2023/12/20 15:34:45 thorpej Exp $");

#include "opt_multiprocessor.h"

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/cpu.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/lwp.h>

#include <machine/nexus.h>
#include <machine/clock.h>
#include <machine/scb.h>
#include <machine/sid.h>
#include <machine/rpb.h>
#include <machine/ka88.h>

#include <dev/cons.h>
#include <vax/vax/gencons.h>

#include "ioconf.h"
#include "locators.h"

static void ka88_memerr(void);
static void ka88_conf(void);
static int ka88_mchk(void *);
static void ka88_steal_pages(void);
static int ka88_gettime(volatile struct timeval *);
static void ka88_settime(volatile struct timeval *);
static void ka88_badaddr(void);

static long *ka88_mcl;
static int mastercpu;

static const char * const ka88_devs[] = { "nmi", NULL };

const struct cpu_dep ka88_calls = {
        .cpu_steal_pages = ka88_steal_pages,
        .cpu_mchk       = ka88_mchk,
        .cpu_memerr     = ka88_memerr,
        .cpu_conf       = ka88_conf,
        .cpu_gettime    = ka88_gettime,
        .cpu_settime    = ka88_settime,
        .cpu_vups       = 6,    /* ~VUPS */
        .cpu_scbsz      = 64,   /* SCB pages */
        .cpu_devs       = ka88_devs,
        .cpu_badaddr    = ka88_badaddr,
};

#if defined(MULTIPROCESSOR)
static void ka88_startslave(struct cpu_info *);
static void ka88_txrx(int, const char *, ...) __printflike(2, 3);
static void ka88_sendstr(int, const char *);
static void ka88_sergeant(int);
static int rxchar(void);
static void ka88_putc(int);
static void ka88_cnintr(void);
cons_decl(gen);

const struct cpu_mp_dep ka88_mp_calls = {
        .cpu_startslave = ka88_startslave,
        .cpu_cnintr = ka88_cnintr,
};
#endif

static void
ka88_conf(void)
{
        ka88_mcl = (void *)vax_map_physmem(0x3e000000, 1);
        printf("Serial number %d, rev %d\n",
            mfpr(PR_SID) & 65535, (mfpr(PR_SID) >> 16) & 127);
#ifdef MULTIPROCESSOR
        mp_dep_call = &ka88_mp_calls;
#endif
}

static int
ka88_cpu_match(device_t parent, cfdata_t cf, void *aux)
{
        struct nmi_attach_args * const na = aux;

        if (cf->cf_loc[NMICF_SLOT] != NMICF_SLOT_DEFAULT &&
            cf->cf_loc[NMICF_SLOT] != na->na_slot)
                return 0;
        if (na->na_slot >= 20)
                return 1;
        return 0;
}

static void
ka88_cpu_attach(device_t parent, device_t self, void *aux)
{
        struct cpu_info *ci;
        struct nmi_attach_args * const na = aux;
        const char *ms, *lr;
        const bool master = (na->na_slot == mastercpu);

        if (((ka88_confdata & KA88_LEFTPRIM) && master) ||
            ((ka88_confdata & KA88_LEFTPRIM) == 0 && !master))
                lr = "left";
        else
                lr = "right";
        ms = (master ? "master" : "slave");

        aprint_normal(": KA88 %s %s\n", lr, ms);
        if (!master) {
#if defined(MULTIPROCESSOR)
                v_putc = ka88_putc;     /* Need special console handling */
                cpu_slavesetup(self, na->na_slot);
#endif
                return;
        }
        ci = curcpu();
        device_set_private(self, ci);
        ci->ci_dev = self;
        ci->ci_cpuid = device_unit(self);
        ci->ci_slotid = na->na_slot;
}

CFATTACH_DECL_NEW(cpu_nmi, 0,
    ka88_cpu_match, ka88_cpu_attach, NULL, NULL);

struct mem_nmi_softc {
        device_t sc_dev;
        bus_space_tag_t sc_iot;
        bus_space_handle_t sc_ioh;
};

static int
ms88_match(device_t parent, cfdata_t cf, void *aux)
{
        struct nmi_attach_args * const na = aux;

        if (cf->cf_loc[NMICF_SLOT] != NMICF_SLOT_DEFAULT &&
            cf->cf_loc[NMICF_SLOT] != na->na_slot)
                return 0;
        if (na->na_slot != 10)
                return 0;
        return 1;
}

static void
ms88_attach(device_t parent, device_t self, void *aux)
{
        struct nmi_attach_args * const na = aux;
        struct mem_nmi_softc * const sc = device_private(self);

        aprint_normal("\n");

        sc->sc_dev = self;
        sc->sc_iot = na->na_iot;
}

CFATTACH_DECL_NEW(mem_nmi, sizeof(struct mem_nmi_softc),
    ms88_match, ms88_attach, NULL, NULL);

static void
ka88_badaddr(void)
{
        volatile int hej;
        /*
         * This is some magic to clear the NMI faults, described
         * in section 7.9 in the VAX 8800 System Maintenance Guide.
         */
        hej = ka88_mcl[5];
        hej = ka88_mcl[0];
        ka88_mcl[0] = 0x04000000;
        mtpr(1, 0x88);
}

static void
ka88_memerr(void)
{
        printf("ka88_memerr\n");
}

struct mc88frame {
        int     mc64_summary;           /* summary parameter */
        int     mc64_va;                /* va register */
        int     mc64_vb;                /* memory address */
        int     mc64_sisr;              /* status word */
        int     mc64_state;             /* error pc */
        int     mc64_sc;                /* micro pc */
        int     mc64_pc;                /* current pc */
        int     mc64_psl;               /* current psl */
};

static int
ka88_mchk(void *cmcf)
{
        return (MCHK_PANIC);
}

#if defined(MULTIPROCESSOR)
#define RXBUF   80
static char rxbuf[RXBUF];
static int got = 0, taken = 0;
static int expect = 0;
#endif
#if 0
/*
 * Receive a character from logical console.
 */
static void
rxcdintr(void *arg)
{
        int c = mfpr(PR_RXCD);

        if (c == 0)
                return;

#if defined(MULTIPROCESSOR)
        if ((c & 0xff) == 0) {
                if (curcpu()->ci_flags & CI_MASTERCPU)
                        ka88_cnintr();
                return;
        }

        if (expect == ((c >> 8) & 0xf))
                rxbuf[got++] = c & 0xff;

        if (got == RXBUF)
                got = 0;
#endif
}
#endif

static void
tocons(int val)
{
        int s = splhigh();

        while ((mfpr(PR_TXCS) & GC_RDY) == 0)  /* Wait until xmit ready */
                ;
        mtpr(val, PR_TXDB);             /* xmit character */
        splx(s);
}

static int
fromcons(int func)
{
        int ret, s = splhigh();

        while (1) {
                while ((mfpr(PR_RXCS) & GC_DON) == 0)
                        ;
                ret = mfpr(PR_RXDB);
                if ((ret & 0xf00) == func)
                        break;
        }
        splx(s);
        return ret;
}

static int
ka88_gettime(volatile struct timeval *tvp)
{
        union {u_int ret;u_char r[4];} u;
        int i, s = splhigh();

        tocons(KA88_COMM|KA88_TOYREAD);
        for (i = 0; i < 4; i++) {
                u.r[i] = fromcons(KA88_TOY) & 255;
        }
        splx(s);
        tvp->tv_sec = u.ret;
        return 0;
}

static void
ka88_settime(volatile struct timeval *tvp)
{
        union {u_int ret;u_char r[4];} u;
        int i, s = splhigh();

        u.ret = tvp->tv_sec - yeartonum(numtoyear(tvp->tv_sec));
        tocons(KA88_COMM|KA88_TOYWRITE);
        for (i = 0; i < 4; i++)
                tocons(KA88_TOY|u.r[i]);
        splx(s);
}

void
ka88_steal_pages(void)
{
        char c = '0', d = '0';
        mtpr(1, PR_COR); /* Cache on */
        tocons(KA88_COMM|KA88_GETCONF);
        ka88_confdata = fromcons(KA88_CONFDATA);
        ka88_confdata = mfpr(PR_RXDB);
        mastercpu = 20;
        if (vax_cputype == VAX_TYP_8NN) {
                if (ka88_confdata & KA88_SMALL) {
                        c = '5';
                        if (ka88_confdata & KA88_SLOW) {
                                vax_boardtype = VAX_BTYP_8500;
                                d = '3';
                        } else {
                                vax_boardtype = VAX_BTYP_8550;
                                d = '5';
                        }
                } else if (ka88_confdata & KA88_SINGLE) {
                        vax_boardtype = VAX_BTYP_8700;
                        c = '7';
                }
        }
        cpu_setmodel("VAX 88%c%c", c, d);
}
        

#if defined(MULTIPROCESSOR)
int
rxchar(void)
{
        int ret;

        if (got == taken)
                return 0;

        ret = rxbuf[taken++];
        if (taken == RXBUF)
                taken = 0;
        return ret;
}

static void
ka88_startslave(struct cpu_info *ci)
{
        const struct pcb *pcb = lwp_getpcb(ci->ci_onproc);
        const int id = ci->ci_slotid;
        int i;

        expect = id;
        /* First empty queue */
        for (i = 0; i < 10000; i++)
                if (rxchar())
                        i = 0;
        ka88_txrx(id, "\020");          /* Send ^P to get attention */
        ka88_txrx(id, "I\r");                   /* Init other end */
        ka88_txrx(id, "D/I 4 %x\r", ci->ci_istack);     /* Interrupt stack */
        ka88_txrx(id, "D/I C %x\r", mfpr(PR_SBR));      /* SBR */
        ka88_txrx(id, "D/I D %x\r", mfpr(PR_SLR));      /* SLR */
        ka88_txrx(id, "D/I 10 %x\r", pcb->pcb_paddr);   /* PCB for idle proc */
        ka88_txrx(id, "D/I 11 %x\r", mfpr(PR_SCBB));    /* SCB */
        ka88_txrx(id, "D/I 38 %x\r", mfpr(PR_MAPEN)); /* Enable MM */
        ka88_txrx(id, "S %x\r", (int)&vax_mp_tramp); /* Start! */
        expect = 0;
        for (i = 0; i < 10000; i++)
                if (ci->ci_flags & CI_RUNNING)
                        break;
        if (i == 10000)
                aprint_error_dev(ci->ci_dev, "(ID %d) failed starting!!\n", id);
}

static void
ka88_txrx(int id, const char *fmt, ...)
{
        char buf[20];
        va_list ap;

        va_start(ap, fmt);
        vsnprintf(buf, sizeof(buf), fmt, ap);
        va_end(ap);
        ka88_sendstr(id, buf);
        ka88_sergeant(id);
}

void
ka88_sendstr(int id, const char *buf)
{
        u_int utchr; /* Ends up in R11 with PCC */
        int ch, i;

        while (*buf) {
                utchr = *buf | id << 8;

                /*
                 * It seems like mtpr to TXCD sets the V flag if it fails.
                 * Cannot check that flag in C...
                 */
#ifdef __GNUC__
                __asm("1:;mtpr %0,$92;bvs 1b" :: "g"(utchr));
#else
                __asm("1:;mtpr r11,$92;bvs 1b");
#endif
                buf++;
                i = 30000;
                while ((ch = rxchar()) == 0 && --i)
                        ;
                if (ch == 0)
                        continue; /* failed */
        }
}

void
ka88_sergeant(int id)
{
        int i, ch, nserg;

        nserg = 0;
        for (i = 0; i < 30000; i++) {
                if ((ch = rxchar()) == 0)
                        continue;
                if (ch == '>')
                        nserg++;
                else
                        nserg = 0;
                i = 0;
                if (nserg == 3)
                        break;
        }
        /* What to do now??? */
}

/*
 * Write to master console.
 * Need no locking here; done in the print functions.
 */
static volatile int ch = 0;

void
ka88_putc(int c)
{
        if (curcpu()->ci_flags & CI_MASTERCPU) {
                gencnputc(0, c);
                return;
        }
        ch = c;
        mtpr(mastercpu << 8, PR_RXCD); /* Send IPI to mastercpu */
        while (ch != 0)
                ; /* Wait for master to handle */
}

/*
 * Got character IPI.
 */
void
ka88_cnintr(void)
{
        if (ch != 0)
                gencnputc(0, ch);
        ch = 0; /* Release slavecpu */
}
#endif