1/*	$NetBSD: kbms_sbdio.c,v 1.15 2021/12/10 20:36:02 andvar Exp $	*/
2
3/*-
4 * Copyright (c) 2004, 2005 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by UCHIYAMA Yasushi.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: kbms_sbdio.c,v 1.15 2021/12/10 20:36:02 andvar Exp $");
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/device.h>
38
39#include <dev/wscons/wsconsio.h>
40#include <dev/wscons/wskbdvar.h>
41#include <dev/wscons/wsmousevar.h>
42
43#include <dev/wscons/wsksymdef.h>
44#include <dev/wscons/wsksymvar.h>
45
46#include <dev/ic/z8530reg.h>
47
48#include <machine/sbdiovar.h>
49
50#include <ews4800mips/dev/ews4800keymap.h>
51
52/* 85C30 keyboard, mouse driver */
53
54struct kbms_reg {
55	volatile uint8_t *kbd_csr;
56	volatile uint8_t *kbd_data;
57	volatile uint8_t *mouse_csr;
58	volatile uint8_t *mouse_data;
59};
60
61enum { MOUSE_PACKET_LEN = 5 };
62struct kbms_softc {
63	device_t sc_dev;
64	device_t sc_wskbd;
65	device_t sc_wsmouse;
66	struct kbms_reg sc_reg;
67	int sc_leds;
68	int sc_flags;
69	int sc_mouse_sig;
70	int sc_mouse_cnt;
71	int8_t sc_mouse_buf[MOUSE_PACKET_LEN];
72};
73
74int kbms_sbdio_match(device_t, cfdata_t, void *);
75void kbms_sbdio_attach(device_t, device_t, void *);
76int kbms_sbdio_intr(void *);
77
78CFATTACH_DECL_NEW(kbms_sbdio, sizeof(struct kbms_softc),
79    kbms_sbdio_match, kbms_sbdio_attach, NULL, NULL);
80
81int kbd_enable(void *, int);
82void kbd_set_leds(void *, int);
83int kbd_ioctl(void *, u_long, void *, int, struct lwp *);
84
85int mouse_enable(void *);
86void mouse_disable(void *);
87int mouse_ioctl(void *, u_long, void *, int, struct lwp *);
88
89bool kbd_init(struct kbms_softc *);
90bool kbd_reset(struct kbms_softc *, int);
91
92void mouse_init(struct kbms_softc *);
93#ifdef MOUSE_DEBUG
94void mouse_debug_print(u_int, int, int);
95#endif
96
97int kbd_sbdio_cnattach(uint32_t, uint32_t);
98void kbd_cngetc(void *, u_int *, int *);
99void kbd_cnpollc(void *, int);
100
101static struct kbms_reg kbms_consreg;
102
103const struct wskbd_consops kbd_consops = {
104	kbd_cngetc,
105	kbd_cnpollc,
106};
107
108const struct wskbd_accessops kbd_accessops = {
109	kbd_enable,
110	kbd_set_leds,
111	kbd_ioctl,
112};
113
114struct wskbd_mapdata kbd_keymapdata = {
115	ews4800kbd_keydesctab,
116	KB_JP,
117};
118
119const struct wsmouse_accessops mouse_accessops = {
120	mouse_enable,
121	mouse_ioctl,
122	mouse_disable,
123};
124
125#define KBMS_PCLK	(9600 * 512)
126
127
128int
129kbms_sbdio_match(device_t parent, cfdata_t cf, void *aux)
130{
131	struct sbdio_attach_args *sa = aux;
132
133	return strcmp(sa->sa_name, "zkbms") ? 0 : 1;
134}
135
136void
137kbms_sbdio_attach(device_t parent, device_t self, void *aux)
138{
139	struct kbms_softc *sc = device_private(self);
140	struct sbdio_attach_args *sa = aux;
141	struct wsmousedev_attach_args ma;
142	struct wskbddev_attach_args ka;
143	struct kbms_reg *reg = &sc->sc_reg;
144	uint8_t *base;
145
146	sc->sc_dev = self;
147	aprint_normal("\n");
148
149	base = (uint8_t *)MIPS_PHYS_TO_KSEG1(sa->sa_addr1);
150	reg->kbd_csr    = base + 0x00;	/* port B */
151	reg->kbd_data   = base + 0x04;
152	reg->mouse_csr  = base + 0x08;	/* port A */
153	reg->mouse_data = base + 0x0c;
154
155	if (reg->kbd_csr  == kbms_consreg.kbd_csr &&
156	    reg->kbd_data == kbms_consreg.kbd_data)
157		ka.console = true;
158	else
159		ka.console = false;
160
161	ka.keymap = &kbd_keymapdata;
162	ka.accessops = &kbd_accessops;
163	ka.accesscookie = self;
164
165	if (kbd_init(sc) == false) {
166		printf("keyboard not connected\n");
167		return;
168	}
169
170	sc->sc_wskbd = config_found(self, &ka, wskbddevprint, CFARGS_NONE);
171
172	ma.accessops = &mouse_accessops;
173	ma.accesscookie = self;
174
175	if (sa->sa_flags == 0x0001)
176		sc->sc_flags = 1;
177	sc->sc_mouse_sig = 0x80;
178	if (sc->sc_flags == 1)	/* ER/TR?? */
179		sc->sc_mouse_sig = 0x88;
180
181	mouse_init(sc);
182	sc->sc_wsmouse = config_found(self, &ma, wsmousedevprint, CFARGS_NONE);
183
184	intr_establish(sa->sa_irq, kbms_sbdio_intr, self);
185}
186
187int
188kbms_sbdio_intr(void *arg)
189{
190	struct kbms_softc *sc = (void *)arg;
191	struct kbms_reg *reg = &sc->sc_reg;
192	int v;
193
194	if (*reg->kbd_csr & ZSRR0_RX_READY) {
195		v = *reg->kbd_data;
196		wskbd_input(sc->sc_wskbd,
197		    v & 0x80 ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN,
198		    v & 0x7f);
199	}
200
201	while (*reg->mouse_csr & ZSRR0_RX_READY) {
202		int8_t *buf = sc->sc_mouse_buf;
203		*reg->mouse_csr = 1;
204		if (((v = *reg->mouse_csr) &
205		    (ZSRR1_FE | ZSRR1_DO | ZSRR1_PE)) != 0) {
206			/* Error occurred. re-initialize */
207			printf("initialize mouse. error=%02x\n", v);
208			mouse_init(sc);
209		} else {
210			v = *reg->mouse_data;
211			if ((sc->sc_mouse_cnt == 0) &&
212			    (v & 0xf8) != sc->sc_mouse_sig) {
213				printf("missing first packet. reset. %x\n", v);
214				mouse_init(sc);
215				continue;
216			}
217			buf[sc->sc_mouse_cnt++] = v;
218
219			if (sc->sc_mouse_cnt == MOUSE_PACKET_LEN) {
220				int x, y;
221				u_int buttons;
222				buttons = ~buf[0] & 0x7;
223				if (sc->sc_flags == 0) {
224					u_int b1 = (buttons & 0x1) << 2;
225					u_int b3 = (buttons & 0x4) >> 2;
226					buttons = (buttons & 0x2) | b1 | b3;
227				}
228				x = buf[1] + buf[3];
229				y = buf[2] + buf[4];
230#ifdef MOUSE_DEBUG
231				mouse_debug_print(buttons, x, y);
232#endif
233				wsmouse_input(sc->sc_wsmouse,
234						buttons,
235						x, y, 0, 0,
236						WSMOUSE_INPUT_DELTA);
237				sc->sc_mouse_cnt = 0;
238			}
239
240		}
241		*reg->mouse_csr = ZSWR1_REQ_RX | ZSWR1_RIE | ZSWR1_RIE_FIRST;
242		(void)*reg->mouse_csr;
243	}
244
245	return 0;
246}
247
248#define	__REG_WR(r, v)							\
249do {									\
250	*csr = (r);							\
251	delay(1);							\
252	*csr = (v);							\
253	delay(1);							\
254} while (/*CONSTCOND*/ 0)
255
256bool
257kbd_init(struct kbms_softc *sc)
258{
259	struct kbms_reg *reg = &sc->sc_reg;
260	volatile uint8_t *csr = reg->kbd_csr;
261	int retry = 2;
262	int reset_retry = 100;
263
264	do {
265		__REG_WR(9, ZSWR9_B_RESET);
266		delay(100);
267		__REG_WR(9, ZSWR9_MASTER_IE | ZSWR9_NO_VECTOR);
268		__REG_WR(1, 0);
269		__REG_WR(4, ZSWR4_CLK_X16 | ZSWR4_ONESB | ZSWR4_PARENB);
270		__REG_WR(12, BPS_TO_TCONST(KBMS_PCLK / 16, 4800));
271		__REG_WR(13, 0);
272		__REG_WR(5, ZSWR5_TX_8 | ZSWR5_RTS);
273		__REG_WR(3, ZSWR3_RX_8);
274		__REG_WR(10, 0);
275		__REG_WR(11, ZSWR11_RXCLK_BAUD | ZSWR11_TXCLK_BAUD |
276		    ZSWR11_TRXC_OUT_ENA | ZSWR11_TRXC_BAUD);
277		__REG_WR(14, ZSWR14_BAUD_FROM_PCLK | ZSWR14_BAUD_ENA);
278		__REG_WR(15, 0);
279		__REG_WR(5, ZSWR5_TX_8 | ZSWR5_TX_ENABLE);
280		__REG_WR(3, ZSWR3_RX_8 | ZSWR3_RX_ENABLE);
281		reset_retry *= 2;
282	} while (!kbd_reset(sc, reset_retry) && --retry > 0);
283
284	if (retry == 0) {
285		printf("keyboard initialize failed.\n");
286		return false;
287	}
288
289	return true;
290}
291
292bool
293kbd_reset(struct kbms_softc *sc, int retry)
294{
295#define	__RETRY_LOOP(x, y)						\
296do {									\
297	for (i = 0; (x) && (i < retry); i++) {				\
298		(void)(y);						\
299		delay(10);						\
300	}								\
301	if (i == retry)							\
302		goto error;						\
303} while (/*CONSTCOND*/ 0)
304	int i;
305	struct kbms_reg *reg = &sc->sc_reg;
306	volatile uint8_t *csr = reg->kbd_csr;
307	volatile uint8_t *data = reg->kbd_data;
308	volatile uint8_t dummy;
309
310	__REG_WR(5, ZSWR5_DTR | ZSWR5_TX_8 | ZSWR5_TX_ENABLE);
311	delay(100);
312	__RETRY_LOOP(*csr & ZSRR0_RX_READY, dummy = *data);
313	*csr = 48;
314	__REG_WR(5, ZSWR5_TX_8 | ZSWR5_TX_ENABLE | ZSWR5_RTS);
315	__RETRY_LOOP((*csr & ZSRR0_RX_READY) == 0, 0);
316	*csr = 1;
317	__RETRY_LOOP((*csr & (ZSRR1_FE | ZSRR1_DO | ZSRR1_PE)) != 0, 0);
318	__RETRY_LOOP(*data != 0xa0, 0);
319	__RETRY_LOOP((*csr & ZSRR0_RX_READY) == 0, 0);
320	*csr = 1;
321	__RETRY_LOOP((*csr & (ZSRR1_FE | ZSRR1_DO | ZSRR1_PE)) != 0, 0);
322	__REG_WR(1, ZSWR1_RIE);
323
324	/* drain buffer */
325	(void)*reg->kbd_data;
326#undef __RETRY_LOOP
327	return true;
328 error:
329	printf("retry failed.\n");
330	return false;
331}
332
333void
334mouse_init(struct kbms_softc *sc)
335{
336	struct kbms_reg *reg = &sc->sc_reg;
337	volatile uint8_t *csr = reg->mouse_csr;
338	volatile uint8_t *data = reg->mouse_data;
339	uint8_t d[] = { 0x02, 0x52, 0x53, 0x3b, 0x4d, 0x54, 0x2c, 0x36, 0x0d };
340	int i;
341
342	__REG_WR(9, ZSWR9_A_RESET);
343	delay(100);
344	__REG_WR(9, ZSWR9_MASTER_IE | ZSWR9_NO_VECTOR);
345	__REG_WR(1, 0);
346	__REG_WR(4, ZSWR4_CLK_X16 | ZSWR4_ONESB);
347	if (sc->sc_flags & 0x1) {
348		__REG_WR(12, BPS_TO_TCONST(KBMS_PCLK / 16, 4800));
349		__REG_WR(13, 0);
350	} else {
351		__REG_WR(12, BPS_TO_TCONST(KBMS_PCLK / 16, 1200));
352		__REG_WR(13, 0);
353	}
354	__REG_WR(5, ZSWR5_DTR | ZSWR5_TX_8 | ZSWR5_RTS);
355	__REG_WR(3, ZSWR3_RX_8);
356	__REG_WR(10, 0);
357	__REG_WR(11, ZSWR11_RXCLK_BAUD | ZSWR11_TXCLK_BAUD |
358	    ZSWR11_TRXC_OUT_ENA | ZSWR11_TRXC_BAUD);
359	__REG_WR(14, ZSWR14_BAUD_FROM_PCLK);
360	__REG_WR(15, 0);
361	__REG_WR(5, ZSWR5_DTR | ZSWR5_TX_8 | ZSWR5_TX_ENABLE);
362	__REG_WR(3, ZSWR3_RX_8 | ZSWR3_RX_ENABLE);
363
364	if (sc->sc_flags & 0x1) {
365		for (i = 0; i < sizeof d; i++) {
366			while ((*csr & ZSRR0_TX_READY) == 0)
367				;
368			*data = d[i];
369		}
370	}
371
372	__REG_WR(1, ZSWR1_RIE);
373}
374
375int
376kbd_enable(void *arg, int on)
377{
378
379	/* always active */
380	return 0;
381}
382
383void
384kbd_set_leds(void *arg, int leds)
385{
386	struct kbms_softc *sc = arg;
387	struct kbms_reg *reg = &sc->sc_reg;
388
389	sc->sc_leds = leds;
390	if (leds & WSKBD_LED_CAPS)
391		*reg->kbd_data = 0x92;
392	else
393		*reg->kbd_data = 0x90;
394}
395
396int
397kbd_ioctl(void *arg, u_long cmd, void *data, int flag, struct lwp *l)
398{
399	struct kbms_softc *sc = arg;
400
401	switch (cmd) {
402	case WSKBDIO_GTYPE:
403		*(int *)data = WSKBD_TYPE_EWS4800;
404		return 0;
405	case WSKBDIO_SETLEDS:
406		kbd_set_leds(arg, *(int *)data);
407		return 0;
408	case WSKBDIO_GETLEDS:
409		*(int *)data = sc->sc_leds;
410		return 0;
411	case WSKBDIO_COMPLEXBELL:
412		return 0;
413	}
414
415	return EPASSTHROUGH;
416}
417
418int
419kbd_sbdio_cnattach(uint32_t csr, uint32_t data)
420{
421	struct kbms_softc __softc, *sc;
422	struct kbms_reg *reg;
423
424	kbms_consreg.kbd_csr  = (void *)csr;
425	kbms_consreg.kbd_data = (void *)data;
426
427	/* setup dummy softc for kbd_init() */
428	sc = &__softc;
429	memset(sc, 0, sizeof(struct kbms_softc));
430	reg = &sc->sc_reg;
431	reg->kbd_csr  = (void *)csr;
432	reg->kbd_data = (void *)data;
433
434	if (kbd_init(sc) == false)
435		return false;
436
437	wskbd_cnattach(&kbd_consops, &kbms_consreg, &kbd_keymapdata);
438	return true;
439}
440
441void
442kbd_cngetc(void *arg, u_int *type, int *data)
443{
444	struct kbms_reg *reg = (void *)arg;
445	int v;
446
447	while ((*reg->kbd_csr & ZSRR0_RX_READY) == 0)
448		;
449	v = *reg->kbd_data;
450	*type = v & 0x80 ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN;
451	*data = v & 0x7f;
452}
453
454void
455kbd_cnpollc(void *arg, int on)
456{
457	static bool __polling = false;
458	static int s;
459
460	if (on && !__polling) {
461		s = splhigh();  /* Disable interrupt driven I/O */
462		__polling = true;
463	} else if (!on && __polling) {
464		__polling = false;
465		splx(s);        /* Enable interrupt driven I/O */
466	}
467}
468
469int
470mouse_enable(void *arg)
471{
472
473	/* always active */
474	return 0;
475}
476
477void
478mouse_disable(void *arg)
479{
480
481	/* always active */
482}
483
484int
485mouse_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
486{
487
488	return EPASSTHROUGH;
489}
490
491#ifdef MOUSE_DEBUG
492void
493mouse_debug_print(u_int buttons, int x, int y)
494{
495#define	MINMAX(x, min, max)						\
496	((x) < (min) ? (min) : ((x) > (max) ? (max) : (x)))
497	static int k, __x, __y;
498	int i, j;
499	char buf[64];
500
501	__x = MINMAX(__x + x, 0, FB_WIDTH);
502	__y = MINMAX(__y + y, 0, FB_HEIGHT);
503	*(uint8_t *)(fb.fb_addr + __x + __y * FB_LINEBYTES) = 0xff;
504
505	snprintf(buf, sizeof(buf), "%8d %8d", x, y);
506	for (i = 0; i < 64 && buf[i]; i++)
507		fb_drawchar(480 + i * 12, k, buf[i]);
508
509	i += 12;
510	for (j = 0x80; j > 0; j >>= 1, i++) {
511		fb_drawchar(480 + i * 12, k, buttons & j ? '|' : '.');
512	}
513
514	k += 24;
515	if (k > 1000)
516		k = 0;
517
518}
519#endif
520