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