1/* $NetBSD: ka88.c,v 1.22 2023/12/20 15:34:45 thorpej Exp $ */ 2 3/* 4 * Copyright (c) 2000 Ludd, University of Lule}, Sweden. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27/* 28 * KA88 specific CPU code. 29 */ 30/* 31 * TODO: 32 * - Machine check code 33 */ 34 35#include <sys/cdefs.h> 36__KERNEL_RCSID(0, "$NetBSD: ka88.c,v 1.22 2023/12/20 15:34:45 thorpej Exp $"); 37 38#include "opt_multiprocessor.h" 39 40#include <sys/param.h> 41#include <sys/systm.h> 42#include <sys/bus.h> 43#include <sys/cpu.h> 44#include <sys/device.h> 45#include <sys/kernel.h> 46#include <sys/lwp.h> 47 48#include <machine/nexus.h> 49#include <machine/clock.h> 50#include <machine/scb.h> 51#include <machine/sid.h> 52#include <machine/rpb.h> 53#include <machine/ka88.h> 54 55#include <dev/cons.h> 56#include <vax/vax/gencons.h> 57 58#include "ioconf.h" 59#include "locators.h" 60 61static void ka88_memerr(void); 62static void ka88_conf(void); 63static int ka88_mchk(void *); 64static void ka88_steal_pages(void); 65static int ka88_gettime(volatile struct timeval *); 66static void ka88_settime(volatile struct timeval *); 67static void ka88_badaddr(void); 68 69static long *ka88_mcl; 70static int mastercpu; 71 72static const char * const ka88_devs[] = { "nmi", NULL }; 73 74const struct cpu_dep ka88_calls = { 75 .cpu_steal_pages = ka88_steal_pages, 76 .cpu_mchk = ka88_mchk, 77 .cpu_memerr = ka88_memerr, 78 .cpu_conf = ka88_conf, 79 .cpu_gettime = ka88_gettime, 80 .cpu_settime = ka88_settime, 81 .cpu_vups = 6, /* ~VUPS */ 82 .cpu_scbsz = 64, /* SCB pages */ 83 .cpu_devs = ka88_devs, 84 .cpu_badaddr = ka88_badaddr, 85}; 86 87#if defined(MULTIPROCESSOR) 88static void ka88_startslave(struct cpu_info *); 89static void ka88_txrx(int, const char *, ...) __printflike(2, 3); 90static void ka88_sendstr(int, const char *); 91static void ka88_sergeant(int); 92static int rxchar(void); 93static void ka88_putc(int); 94static void ka88_cnintr(void); 95cons_decl(gen); 96 97const struct cpu_mp_dep ka88_mp_calls = { 98 .cpu_startslave = ka88_startslave, 99 .cpu_cnintr = ka88_cnintr, 100}; 101#endif 102 103static void 104ka88_conf(void) 105{ 106 ka88_mcl = (void *)vax_map_physmem(0x3e000000, 1); 107 printf("Serial number %d, rev %d\n", 108 mfpr(PR_SID) & 65535, (mfpr(PR_SID) >> 16) & 127); 109#ifdef MULTIPROCESSOR 110 mp_dep_call = &ka88_mp_calls; 111#endif 112} 113 114static int 115ka88_cpu_match(device_t parent, cfdata_t cf, void *aux) 116{ 117 struct nmi_attach_args * const na = aux; 118 119 if (cf->cf_loc[NMICF_SLOT] != NMICF_SLOT_DEFAULT && 120 cf->cf_loc[NMICF_SLOT] != na->na_slot) 121 return 0; 122 if (na->na_slot >= 20) 123 return 1; 124 return 0; 125} 126 127static void 128ka88_cpu_attach(device_t parent, device_t self, void *aux) 129{ 130 struct cpu_info *ci; 131 struct nmi_attach_args * const na = aux; 132 const char *ms, *lr; 133 const bool master = (na->na_slot == mastercpu); 134 135 if (((ka88_confdata & KA88_LEFTPRIM) && master) || 136 ((ka88_confdata & KA88_LEFTPRIM) == 0 && !master)) 137 lr = "left"; 138 else 139 lr = "right"; 140 ms = (master ? "master" : "slave"); 141 142 aprint_normal(": KA88 %s %s\n", lr, ms); 143 if (!master) { 144#if defined(MULTIPROCESSOR) 145 v_putc = ka88_putc; /* Need special console handling */ 146 cpu_slavesetup(self, na->na_slot); 147#endif 148 return; 149 } 150 ci = curcpu(); 151 device_set_private(self, ci); 152 ci->ci_dev = self; 153 ci->ci_cpuid = device_unit(self); 154 ci->ci_slotid = na->na_slot; 155} 156 157CFATTACH_DECL_NEW(cpu_nmi, 0, 158 ka88_cpu_match, ka88_cpu_attach, NULL, NULL); 159 160struct mem_nmi_softc { 161 device_t sc_dev; 162 bus_space_tag_t sc_iot; 163 bus_space_handle_t sc_ioh; 164}; 165 166static int 167ms88_match(device_t parent, cfdata_t cf, void *aux) 168{ 169 struct nmi_attach_args * const na = aux; 170 171 if (cf->cf_loc[NMICF_SLOT] != NMICF_SLOT_DEFAULT && 172 cf->cf_loc[NMICF_SLOT] != na->na_slot) 173 return 0; 174 if (na->na_slot != 10) 175 return 0; 176 return 1; 177} 178 179static void 180ms88_attach(device_t parent, device_t self, void *aux) 181{ 182 struct nmi_attach_args * const na = aux; 183 struct mem_nmi_softc * const sc = device_private(self); 184 185 aprint_normal("\n"); 186 187 sc->sc_dev = self; 188 sc->sc_iot = na->na_iot; 189} 190 191CFATTACH_DECL_NEW(mem_nmi, sizeof(struct mem_nmi_softc), 192 ms88_match, ms88_attach, NULL, NULL); 193 194static void 195ka88_badaddr(void) 196{ 197 volatile int hej; 198 /* 199 * This is some magic to clear the NMI faults, described 200 * in section 7.9 in the VAX 8800 System Maintenance Guide. 201 */ 202 hej = ka88_mcl[5]; 203 hej = ka88_mcl[0]; 204 ka88_mcl[0] = 0x04000000; 205 mtpr(1, 0x88); 206} 207 208static void 209ka88_memerr(void) 210{ 211 printf("ka88_memerr\n"); 212} 213 214struct mc88frame { 215 int mc64_summary; /* summary parameter */ 216 int mc64_va; /* va register */ 217 int mc64_vb; /* memory address */ 218 int mc64_sisr; /* status word */ 219 int mc64_state; /* error pc */ 220 int mc64_sc; /* micro pc */ 221 int mc64_pc; /* current pc */ 222 int mc64_psl; /* current psl */ 223}; 224 225static int 226ka88_mchk(void *cmcf) 227{ 228 return (MCHK_PANIC); 229} 230 231#if defined(MULTIPROCESSOR) 232#define RXBUF 80 233static char rxbuf[RXBUF]; 234static int got = 0, taken = 0; 235static int expect = 0; 236#endif 237#if 0 238/* 239 * Receive a character from logical console. 240 */ 241static void 242rxcdintr(void *arg) 243{ 244 int c = mfpr(PR_RXCD); 245 246 if (c == 0) 247 return; 248 249#if defined(MULTIPROCESSOR) 250 if ((c & 0xff) == 0) { 251 if (curcpu()->ci_flags & CI_MASTERCPU) 252 ka88_cnintr(); 253 return; 254 } 255 256 if (expect == ((c >> 8) & 0xf)) 257 rxbuf[got++] = c & 0xff; 258 259 if (got == RXBUF) 260 got = 0; 261#endif 262} 263#endif 264 265static void 266tocons(int val) 267{ 268 int s = splhigh(); 269 270 while ((mfpr(PR_TXCS) & GC_RDY) == 0) /* Wait until xmit ready */ 271 ; 272 mtpr(val, PR_TXDB); /* xmit character */ 273 splx(s); 274} 275 276static int 277fromcons(int func) 278{ 279 int ret, s = splhigh(); 280 281 while (1) { 282 while ((mfpr(PR_RXCS) & GC_DON) == 0) 283 ; 284 ret = mfpr(PR_RXDB); 285 if ((ret & 0xf00) == func) 286 break; 287 } 288 splx(s); 289 return ret; 290} 291 292static int 293ka88_gettime(volatile struct timeval *tvp) 294{ 295 union {u_int ret;u_char r[4];} u; 296 int i, s = splhigh(); 297 298 tocons(KA88_COMM|KA88_TOYREAD); 299 for (i = 0; i < 4; i++) { 300 u.r[i] = fromcons(KA88_TOY) & 255; 301 } 302 splx(s); 303 tvp->tv_sec = u.ret; 304 return 0; 305} 306 307static void 308ka88_settime(volatile struct timeval *tvp) 309{ 310 union {u_int ret;u_char r[4];} u; 311 int i, s = splhigh(); 312 313 u.ret = tvp->tv_sec - yeartonum(numtoyear(tvp->tv_sec)); 314 tocons(KA88_COMM|KA88_TOYWRITE); 315 for (i = 0; i < 4; i++) 316 tocons(KA88_TOY|u.r[i]); 317 splx(s); 318} 319 320void 321ka88_steal_pages(void) 322{ 323 char c = '0', d = '0'; 324 mtpr(1, PR_COR); /* Cache on */ 325 tocons(KA88_COMM|KA88_GETCONF); 326 ka88_confdata = fromcons(KA88_CONFDATA); 327 ka88_confdata = mfpr(PR_RXDB); 328 mastercpu = 20; 329 if (vax_cputype == VAX_TYP_8NN) { 330 if (ka88_confdata & KA88_SMALL) { 331 c = '5'; 332 if (ka88_confdata & KA88_SLOW) { 333 vax_boardtype = VAX_BTYP_8500; 334 d = '3'; 335 } else { 336 vax_boardtype = VAX_BTYP_8550; 337 d = '5'; 338 } 339 } else if (ka88_confdata & KA88_SINGLE) { 340 vax_boardtype = VAX_BTYP_8700; 341 c = '7'; 342 } 343 } 344 cpu_setmodel("VAX 88%c%c", c, d); 345} 346 347 348#if defined(MULTIPROCESSOR) 349int 350rxchar(void) 351{ 352 int ret; 353 354 if (got == taken) 355 return 0; 356 357 ret = rxbuf[taken++]; 358 if (taken == RXBUF) 359 taken = 0; 360 return ret; 361} 362 363static void 364ka88_startslave(struct cpu_info *ci) 365{ 366 const struct pcb *pcb = lwp_getpcb(ci->ci_onproc); 367 const int id = ci->ci_slotid; 368 int i; 369 370 expect = id; 371 /* First empty queue */ 372 for (i = 0; i < 10000; i++) 373 if (rxchar()) 374 i = 0; 375 ka88_txrx(id, "\020"); /* Send ^P to get attention */ 376 ka88_txrx(id, "I\r"); /* Init other end */ 377 ka88_txrx(id, "D/I 4 %x\r", ci->ci_istack); /* Interrupt stack */ 378 ka88_txrx(id, "D/I C %x\r", mfpr(PR_SBR)); /* SBR */ 379 ka88_txrx(id, "D/I D %x\r", mfpr(PR_SLR)); /* SLR */ 380 ka88_txrx(id, "D/I 10 %x\r", pcb->pcb_paddr); /* PCB for idle proc */ 381 ka88_txrx(id, "D/I 11 %x\r", mfpr(PR_SCBB)); /* SCB */ 382 ka88_txrx(id, "D/I 38 %x\r", mfpr(PR_MAPEN)); /* Enable MM */ 383 ka88_txrx(id, "S %x\r", (int)&vax_mp_tramp); /* Start! */ 384 expect = 0; 385 for (i = 0; i < 10000; i++) 386 if (ci->ci_flags & CI_RUNNING) 387 break; 388 if (i == 10000) 389 aprint_error_dev(ci->ci_dev, "(ID %d) failed starting!!\n", id); 390} 391 392static void 393ka88_txrx(int id, const char *fmt, ...) 394{ 395 char buf[20]; 396 va_list ap; 397 398 va_start(ap, fmt); 399 vsnprintf(buf, sizeof(buf), fmt, ap); 400 va_end(ap); 401 ka88_sendstr(id, buf); 402 ka88_sergeant(id); 403} 404 405void 406ka88_sendstr(int id, const char *buf) 407{ 408 u_int utchr; /* Ends up in R11 with PCC */ 409 int ch, i; 410 411 while (*buf) { 412 utchr = *buf | id << 8; 413 414 /* 415 * It seems like mtpr to TXCD sets the V flag if it fails. 416 * Cannot check that flag in C... 417 */ 418#ifdef __GNUC__ 419 __asm("1:;mtpr %0,$92;bvs 1b" :: "g"(utchr)); 420#else 421 __asm("1:;mtpr r11,$92;bvs 1b"); 422#endif 423 buf++; 424 i = 30000; 425 while ((ch = rxchar()) == 0 && --i) 426 ; 427 if (ch == 0) 428 continue; /* failed */ 429 } 430} 431 432void 433ka88_sergeant(int id) 434{ 435 int i, ch, nserg; 436 437 nserg = 0; 438 for (i = 0; i < 30000; i++) { 439 if ((ch = rxchar()) == 0) 440 continue; 441 if (ch == '>') 442 nserg++; 443 else 444 nserg = 0; 445 i = 0; 446 if (nserg == 3) 447 break; 448 } 449 /* What to do now??? */ 450} 451 452/* 453 * Write to master console. 454 * Need no locking here; done in the print functions. 455 */ 456static volatile int ch = 0; 457 458void 459ka88_putc(int c) 460{ 461 if (curcpu()->ci_flags & CI_MASTERCPU) { 462 gencnputc(0, c); 463 return; 464 } 465 ch = c; 466 mtpr(mastercpu << 8, PR_RXCD); /* Send IPI to mastercpu */ 467 while (ch != 0) 468 ; /* Wait for master to handle */ 469} 470 471/* 472 * Got character IPI. 473 */ 474void 475ka88_cnintr(void) 476{ 477 if (ch != 0) 478 gencnputc(0, ch); 479 ch = 0; /* Release slavecpu */ 480} 481#endif 482