1219888Sed/*	$NetBSD: vrkiu.c,v 1.41 2021/08/07 16:18:54 thorpej Exp $	*/
2219888Sed
3219888Sed/*-
4219888Sed * Copyright (c) 1999 SASAKI Takesi All rights reserved.
5219888Sed * Copyright (c) 1999, 2000, 2002 TAKEMRUA, Shin All rights reserved.
6219888Sed * Copyright (c) 1999 PocketBSD Project. All rights reserved.
7219888Sed *
8219888Sed * Redistribution and use in source and binary forms, with or without
9219888Sed * modification, are permitted provided that the following conditions
10219888Sed * are met:
11219888Sed * 1. Redistributions of source code must retain the above copyright
12219888Sed *    notice, this list of conditions and the following disclaimer.
13219888Sed * 2. Redistributions in binary form must reproduce the above copyright
14219888Sed *    notice, this list of conditions and the following disclaimer in the
15219888Sed *    documentation and/or other materials provided with the distribution.
16219888Sed * 3. All advertising materials mentioning features or use of this software
17219888Sed *    must display the following acknowledgement:
18219888Sed *	This product includes software developed by the PocketBSD project
19219888Sed *	and its contributors.
20219888Sed * 4. Neither the name of the project nor the names of its contributors
21219888Sed *    may be used to endorse or promote products derived from this software
22219888Sed *    without specific prior written permission.
23219888Sed *
24219888Sed * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25219888Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26219888Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27219888Sed * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28219888Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29219888Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30219888Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31219888Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32219888Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33219888Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34219888Sed * SUCH DAMAGE.
35219888Sed *
36219888Sed */
37219888Sed
38219888Sed#include <sys/cdefs.h>
39219888Sed__KERNEL_RCSID(0, "$NetBSD: vrkiu.c,v 1.41 2021/08/07 16:18:54 thorpej Exp $");
40219888Sed
41219888Sed#include <sys/param.h>
42219888Sed#include <sys/tty.h>
43219888Sed#include <sys/systm.h>
44267978Smarius#include <sys/device.h>
45267978Smarius#include <sys/conf.h>
46267978Smarius#include <sys/kernel.h>
47219888Sed#include <sys/proc.h>
48219888Sed
49219888Sed#include <machine/intr.h>
50219888Sed#include <machine/cpu.h>
51219888Sed#include <machine/bus.h>
52219888Sed#include <machine/platid.h>
53219888Sed#include <machine/platid_mask.h>
54219888Sed
55219888Sed#include <dev/hpc/hpckbdvar.h>
56219888Sed
57219888Sed#include <hpcmips/vr/vr.h>
58219888Sed#include <hpcmips/vr/vripif.h>
59219888Sed#include <hpcmips/vr/vrkiureg.h>
60219888Sed#include <hpcmips/vr/vrkiuvar.h>
61219888Sed#include <hpcmips/vr/icureg.h>
62219888Sed
63219888Sed#include "opt_wsdisplay_compat.h"
64219888Sed#include "opt_pckbd_layout.h"
65219888Sed#include <dev/wscons/wsconsio.h>
66219888Sed#include <dev/wscons/wskbdvar.h>
67219888Sed#include <dev/wscons/wsksymdef.h>
68259667Sed#include <dev/wscons/wsksymvar.h>
69219888Sed#ifdef WSDISPLAY_COMPAT_RAWKBD
70219888Sed#include <dev/hpc/pckbd_encode.h>
71219888Sed#endif
72219888Sed
73219888Sed#define VRKIUDEBUG
74219888Sed#ifdef VRKIUDEBUG
75259667Sedint vrkiu_debug = 0;
76219888Sed#define DPRINTF(arg) if (vrkiu_debug) printf arg;
77267978Smarius#else
78219888Sed#define	DPRINTF(arg)
79267978Smarius#endif
80267978Smarius
81219888Sedstatic int vrkiumatch(device_t, cfdata_t, void *);
82267978Smariusstatic void vrkiuattach(device_t, device_t, void *);
83267978Smarius
84219888Sedint vrkiu_intr(void *);
85219888Sed
86267978Smariusstatic int vrkiu_init(struct vrkiu_chip *, bus_space_tag_t, bus_space_handle_t);
87267978Smariusstatic void vrkiu_write(struct vrkiu_chip *, int, unsigned short);
88267978Smariusstatic unsigned short vrkiu_read(struct vrkiu_chip *, int);
89267978Smariusstatic int vrkiu_is_console(bus_space_tag_t, bus_space_handle_t);
90267978Smariusstatic void vrkiu_scan(struct vrkiu_chip *);
91267978Smariusstatic int countbits(int);
92267978Smariusstatic void eliminate_phantom_keys(struct vrkiu_chip *, unsigned short *);
93267978Smariusstatic int vrkiu_poll(void*);
94267978Smariusstatic int vrkiu_input_establish(void*, struct hpckbd_if*);
95267978Smarius
96267978SmariusCFATTACH_DECL_NEW(vrkiu, sizeof(struct vrkiu_softc),
97267978Smarius    vrkiumatch, vrkiuattach, NULL, NULL);
98267978Smarius
99267978Smariusstruct vrkiu_chip *vrkiu_consdata = NULL;
100267978Smarius
101267978Smariusstatic inline void
102267978Smariusvrkiu_write(struct vrkiu_chip *chip, int port, unsigned short val)
103267978Smarius{
104267978Smarius
105267978Smarius	bus_space_write_2(chip->kc_iot, chip->kc_ioh, port, val);
106267978Smarius}
107267978Smarius
108267978Smariusstatic inline unsigned short
109267978Smariusvrkiu_read(struct vrkiu_chip *chip, int port)
110267978Smarius{
111267978Smarius
112267978Smarius	return (bus_space_read_2(chip->kc_iot, chip->kc_ioh, port));
113267978Smarius}
114267978Smarius
115267978Smariusstatic inline int
116267978Smariusvrkiu_is_console(bus_space_tag_t iot, bus_space_handle_t ioh)
117267978Smarius{
118267978Smarius
119267978Smarius	if (vrkiu_consdata &&
120267978Smarius	    vrkiu_consdata->kc_iot == iot &&
121267978Smarius	    vrkiu_consdata->kc_ioh == ioh) {
122267978Smarius		return (1);
123267978Smarius	} else {
124267978Smarius		return (0);
125267978Smarius	}
126267978Smarius}
127267978Smarius
128267978Smariusstatic int
129267978Smariusvrkiumatch(device_t parent, cfdata_t cf, void *aux)
130267978Smarius{
131267978Smarius
132267978Smarius	return (1);
133267978Smarius}
134267978Smarius
135267978Smariusstatic void
136267978Smariusvrkiuattach(device_t parent, device_t self, void *aux)
137267978Smarius{
138267978Smarius	struct vrkiu_softc *sc = device_private(self);
139267978Smarius	struct vrip_attach_args *va = aux;
140267978Smarius	struct hpckbd_attach_args haa;
141267978Smarius	int isconsole, res;
142219888Sed
143219888Sed	bus_space_tag_t iot = va->va_iot;
144219888Sed	bus_space_handle_t ioh;
145219888Sed
146219888Sed	if (va->va_parent_ioh != 0)
147219888Sed		res = bus_space_subregion(iot, va->va_parent_ioh, va->va_addr,
148219888Sed		    va->va_size, &ioh);
149219888Sed	else
150219888Sed		res = bus_space_map(iot, va->va_addr, 1, 0, &ioh);
151219888Sed	if (res != 0) {
152219888Sed		printf(": can't map bus space\n");
153219888Sed		return;
154219888Sed	}
155219888Sed
156219888Sed	isconsole = vrkiu_is_console(iot, ioh);
157219888Sed	if (isconsole) {
158270705Sdumbbell		sc->sc_chip = vrkiu_consdata;
159270705Sdumbbell	} else {
160270705Sdumbbell		sc->sc_chip = &sc->sc_chip_body;
161219888Sed		vrkiu_init(sc->sc_chip, iot, ioh);
162219888Sed	}
163219888Sed	sc->sc_chip->kc_sc = sc;
164259777Sray
165259777Sray	if (!(sc->sc_handler =
166219888Sed	    vrip_intr_establish(va->va_vc, va->va_unit, 0, IPL_TTY,
167219888Sed		vrkiu_intr, sc))) {
168219888Sed		printf (": can't map interrupt line.\n");
169219888Sed		return;
170219888Sed	}
171219888Sed	/* Level2 register setting */
172219888Sed	vrip_intr_setmask2(va->va_vc, sc->sc_handler, KIUINT_KDATRDY, 1);
173219888Sed
174219888Sed	printf("\n");
175219888Sed
176219888Sed	/* attach hpckbd */
177219888Sed	haa.haa_ic = &sc->sc_chip->kc_if;
178219888Sed	config_found(self, &haa, hpckbd_print, CFARGS_NONE);
179219888Sed}
180259777Sray
181270705Sdumbbell/*
182270705Sdumbbell * initialize device
183270705Sdumbbell */
184270705Sdumbbellstatic int
185219888Sedvrkiu_init(struct vrkiu_chip *chip, bus_space_tag_t iot,
186219888Sed    bus_space_handle_t ioh)
187219888Sed{
188259777Sray
189219888Sed	memset(chip, 0, sizeof(struct vrkiu_chip));
190219888Sed	chip->kc_iot = iot;
191219888Sed	chip->kc_ioh = ioh;
192219888Sed	chip->kc_enabled = 0;
193219888Sed
194219888Sed	chip->kc_if.hii_ctx		= chip;
195219888Sed	chip->kc_if.hii_establish	= vrkiu_input_establish;
196219888Sed	chip->kc_if.hii_poll		= vrkiu_poll;
197219888Sed
198219888Sed	/* set KIU */
199219888Sed	vrkiu_write(chip, KIURST, 1);   /* reset */
200219888Sed	vrkiu_write(chip, KIUSCANLINE, 0); /* 96keys */
201219888Sed	vrkiu_write(chip, KIUWKS, 0x18a4); /* XXX: scan timing! */
202219888Sed	vrkiu_write(chip, KIUWKI, 450);
203256143Sray	vrkiu_write(chip, KIUSCANREP, 0x8023);
204219888Sed	/* KEYEN | STPREP = 2 | ATSTP | ATSCAN */
205219888Sed
206219888Sed	return (0);
207219888Sed}
208219888Sed
209219888Sedint
210273932Sdumbbellvrkiu_intr(void *arg)
211256897Sray{
212267978Smarius        struct vrkiu_softc *sc = arg;
213219888Sed
214219888Sed	/* When key scan finisshed, this entry is called. */
215219888Sed#if 0
216219888Sed	DPRINTF(("vrkiu_intr: intr=%x scan=%x\n",
217219888Sed	    vrkiu_read(sc->sc_chip, KIUINT) & 7,
218219888Sed	    vrkiu_read(sc->sc_chip, KIUSCANS) & KIUSCANS_SSTAT_MASK));
219256143Sray#endif
220256143Sray
221219888Sed	/*
222256143Sray	 * First, we must clear the interrupt register because
223219888Sed	 * vrkiu_scan() may takes long time if a bitmap screen
224219888Sed	 * scrolls up and it makes us to miss some key release
225219888Sed	 * event.
226219888Sed	 */
227219888Sed	vrkiu_write(sc->sc_chip, KIUINT, 0x7); /* Clear all interrupt */
228219888Sed
229219888Sed#if 1
230256143Sray	/* just return if kiu is scanning keyboard. */
231219888Sed	if ((vrkiu_read(sc->sc_chip, KIUSCANS) & KIUSCANS_SSTAT_MASK) ==
232219888Sed	    KIUSCANS_SSTAT_SCANNING)
233219888Sed		return (0);
234219888Sed#endif
235	vrkiu_scan(sc->sc_chip);
236
237	return (0);
238}
239
240static int
241countbits(int d)
242{
243	int i, n;
244
245	for (i = 0, n = 0; i < NBBY; i++)
246		if (d & (1 << i))
247			n++;
248	return (n);
249}
250
251static void
252eliminate_phantom_keys(struct vrkiu_chip *chip, unsigned short *scandata)
253{
254	unsigned char inkey[KIU_NSCANLINE], *prevkey, *reskey;
255	int i, j;
256#ifdef VRKIUDEBUG
257	int modified = 0;
258	static int prevmod = 0;
259#endif
260
261	reskey = (unsigned char *)scandata;
262	for (i = 0; i < KIU_NSCANLINE; i++)
263		inkey[i] = reskey[i];
264	prevkey = (unsigned char *)chip->kc_scandata;
265
266	for (i = 0; i < KIU_NSCANLINE; i++) {
267		if (countbits(inkey[i]) > 1) {
268			for (j = 0; j < KIU_NSCANLINE; j++) {
269				if (i != j && (inkey[i] & inkey[j])) {
270#ifdef VRKIUDEBUG
271					modified = 1;
272					if (!prevmod) {
273						DPRINTF(("vrkiu_scan: %x:%02x->%02x",
274						    i, inkey[i], prevkey[i]));
275						DPRINTF(("  %x:%02x->%02x\n",
276						    j, inkey[j], prevkey[j]));
277					}
278#endif
279					reskey[i] = prevkey[i];
280					reskey[j] = prevkey[j];
281				}
282			}
283		}
284	}
285#ifdef VRKIUDEBUG
286	prevmod = modified;
287#endif
288}
289
290static void
291vrkiu_scan(struct vrkiu_chip* chip)
292{
293	int i, j, modified, mask;
294	unsigned short scandata[KIU_NSCANLINE/2];
295
296	if (!chip->kc_enabled)
297		return;
298
299	for (i = 0; i < KIU_NSCANLINE / 2; i++) {
300		scandata[i] = vrkiu_read(chip, KIUDATP + i * 2);
301	}
302	eliminate_phantom_keys(chip, scandata);
303
304	for (i = 0; i < KIU_NSCANLINE / 2; i++) {
305		modified = scandata[i] ^ chip->kc_scandata[i];
306		chip->kc_scandata[i] = scandata[i];
307		mask = 1;
308		for (j = 0; j < 16; j++, mask <<= 1) {
309			/*
310			 * Simultaneous keypresses are resolved by registering
311			 * the one with the lowest bit index first.
312			 */
313			if (modified & mask) {
314				int key = i * 16 + j;
315				DPRINTF(("vrkiu_scan: %s(%d,%d)\n",
316				    (scandata[i] & mask) ? "down" : "up",
317				    i, j));
318				hpckbd_input(chip->kc_hpckbd,
319				    (scandata[i] & mask), key);
320			}
321		}
322	}
323}
324
325/* called from bicons.c */
326int
327vrkiu_getc(void)
328{
329	static int flag = 1;
330
331	/*
332	 * XXX, currently
333	 */
334	if (flag) {
335		flag = 0;
336		printf("%s(%d): vrkiu_getc() is not implemented\n",
337		    __FILE__, __LINE__);
338	}
339	return (0);
340}
341
342/*
343 * hpckbd interface routines
344 */
345int
346vrkiu_input_establish(void *ic, struct hpckbd_if *kbdif)
347{
348	struct vrkiu_chip *kc = ic;
349
350	/* save hpckbd interface */
351	kc->kc_hpckbd = kbdif;
352
353	kc->kc_enabled = 1;
354
355	return (0);
356}
357
358int
359vrkiu_poll(void *ic)
360{
361	struct vrkiu_chip *kc = ic;
362
363#if 1
364	/* wait until kiu completes keyboard scan. */
365	while ((vrkiu_read(kc, KIUSCANS) & KIUSCANS_SSTAT_MASK) ==
366	    KIUSCANS_SSTAT_SCANNING)
367		/* wait until kiu completes keyboard scan */;
368#endif
369
370	vrkiu_scan(kc);
371
372	return (0);
373}
374
375/*
376 * console support routine
377 */
378int
379vrkiu_cnattach(bus_space_tag_t iot, int iobase)
380{
381	static struct vrkiu_chip vrkiu_consdata_body;
382	bus_space_handle_t ioh;
383
384	if (vrkiu_consdata) {
385		panic("vrkiu is already attached as the console");
386	}
387	if (bus_space_map(iot, iobase, 1, 0, &ioh)) {
388		printf("%s(%d): can't map bus space\n", __FILE__, __LINE__);
389		return (-1);
390	}
391
392	if (vrkiu_init(&vrkiu_consdata_body, iot, ioh) != 0) {
393		DPRINTF(("%s(%d): vrkiu_init() failed\n", __FILE__, __LINE__));
394		return (-1);
395	}
396	vrkiu_consdata = &vrkiu_consdata_body;
397
398	hpckbd_cnattach(&vrkiu_consdata_body.kc_if);
399
400	return (0);
401}
402