1/*	$NetBSD: vrkiu.c,v 1.37 2005/12/11 12:17:34 christos Exp $	*/
2
3/*-
4 * Copyright (c) 1999 SASAKI Takesi All rights reserved.
5 * Copyright (c) 1999, 2000, 2002 TAKEMRUA, Shin All rights reserved.
6 * Copyright (c) 1999 PocketBSD Project. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the PocketBSD project
19 *	and its contributors.
20 * 4. Neither the name of the project nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 */
37
38#include <sys/cdefs.h>
39__KERNEL_RCSID(0, "$NetBSD: vrkiu.c,v 1.37 2005/12/11 12:17:34 christos Exp $");
40
41#include <sys/param.h>
42#include <sys/tty.h>
43#include <sys/systm.h>
44#include <sys/device.h>
45#include <sys/conf.h>
46#include <sys/kernel.h>
47#include <sys/proc.h>
48
49#include <machine/intr.h>
50#include <machine/cpu.h>
51#include <machine/bus.h>
52#include <machine/platid.h>
53#include <machine/platid_mask.h>
54
55#include <dev/hpc/hpckbdvar.h>
56
57#include <hpcmips/vr/vr.h>
58#include <hpcmips/vr/vripif.h>
59#include <hpcmips/vr/vrkiureg.h>
60#include <hpcmips/vr/vrkiuvar.h>
61#include <hpcmips/vr/icureg.h>
62
63#include "opt_wsdisplay_compat.h"
64#include "opt_pckbd_layout.h"
65#include <dev/wscons/wsconsio.h>
66#include <dev/wscons/wskbdvar.h>
67#include <dev/wscons/wsksymdef.h>
68#include <dev/wscons/wsksymvar.h>
69#ifdef WSDISPLAY_COMPAT_RAWKBD
70#include <dev/hpc/pckbd_encode.h>
71#endif
72
73#define VRKIUDEBUG
74#ifdef VRKIUDEBUG
75int vrkiu_debug = 0;
76#define DPRINTF(arg) if (vrkiu_debug) printf arg;
77#else
78#define	DPRINTF(arg)
79#endif
80
81static int vrkiumatch(struct device *, struct cfdata *, void *);
82static void vrkiuattach(struct device *, struct device *, void *);
83
84int vrkiu_intr(void *);
85
86static int vrkiu_init(struct vrkiu_chip *, bus_space_tag_t, bus_space_handle_t);
87static void vrkiu_write(struct vrkiu_chip *, int, unsigned short);
88static unsigned short vrkiu_read(struct vrkiu_chip *, int);
89static int vrkiu_is_console(bus_space_tag_t, bus_space_handle_t);
90static void vrkiu_scan(struct vrkiu_chip *);
91static int countbits(int);
92static void eliminate_phantom_keys(struct vrkiu_chip *, unsigned short *);
93static int vrkiu_poll(void*);
94static int vrkiu_input_establish(void*, struct hpckbd_if*);
95
96CFATTACH_DECL(vrkiu, sizeof(struct vrkiu_softc),
97    vrkiumatch, vrkiuattach, NULL, NULL);
98
99struct vrkiu_chip *vrkiu_consdata = NULL;
100
101static inline void
102vrkiu_write(struct vrkiu_chip *chip, int port, unsigned short val)
103{
104
105	bus_space_write_2(chip->kc_iot, chip->kc_ioh, port, val);
106}
107
108static inline unsigned short
109vrkiu_read(struct vrkiu_chip *chip, int port)
110{
111
112	return (bus_space_read_2(chip->kc_iot, chip->kc_ioh, port));
113}
114
115static inline int
116vrkiu_is_console(bus_space_tag_t iot, bus_space_handle_t ioh)
117{
118
119	if (vrkiu_consdata &&
120	    vrkiu_consdata->kc_iot == iot &&
121	    vrkiu_consdata->kc_ioh == ioh) {
122		return (1);
123	} else {
124		return (0);
125	}
126}
127
128static int
129vrkiumatch(struct device *parent, struct cfdata *cf, void *aux)
130{
131
132	return (1);
133}
134
135static void
136vrkiuattach(struct device *parent, struct device *self, void *aux)
137{
138	struct vrkiu_softc *sc = (struct vrkiu_softc *)self;
139	struct vrip_attach_args *va = aux;
140	struct hpckbd_attach_args haa;
141	int isconsole, res;
142
143	bus_space_tag_t iot = va->va_iot;
144	bus_space_handle_t ioh;
145
146	if (va->va_parent_ioh != 0)
147		res = bus_space_subregion(iot, va->va_parent_ioh, va->va_addr,
148		    va->va_size, &ioh);
149	else
150		res = bus_space_map(iot, va->va_addr, 1, 0, &ioh);
151	if (res != 0) {
152		printf(": can't map bus space\n");
153		return;
154	}
155
156	isconsole = vrkiu_is_console(iot, ioh);
157	if (isconsole) {
158		sc->sc_chip = vrkiu_consdata;
159	} else {
160		sc->sc_chip = &sc->sc_chip_body;
161		vrkiu_init(sc->sc_chip, iot, ioh);
162	}
163	sc->sc_chip->kc_sc = sc;
164
165	if (!(sc->sc_handler =
166	    vrip_intr_establish(va->va_vc, va->va_unit, 0, IPL_TTY,
167		vrkiu_intr, sc))) {
168		printf (": can't map interrupt line.\n");
169		return;
170	}
171	/* Level2 register setting */
172	vrip_intr_setmask2(va->va_vc, sc->sc_handler, KIUINT_KDATRDY, 1);
173
174	printf("\n");
175
176	/* attach hpckbd */
177	haa.haa_ic = &sc->sc_chip->kc_if;
178	config_found(self, &haa, hpckbd_print);
179}
180
181/*
182 * initialize device
183 */
184static int
185vrkiu_init(struct vrkiu_chip *chip, bus_space_tag_t iot,
186    bus_space_handle_t ioh)
187{
188
189	memset(chip, 0, sizeof(struct vrkiu_chip));
190	chip->kc_iot = iot;
191	chip->kc_ioh = ioh;
192	chip->kc_enabled = 0;
193
194	chip->kc_if.hii_ctx		= chip;
195	chip->kc_if.hii_establish	= vrkiu_input_establish;
196	chip->kc_if.hii_poll		= vrkiu_poll;
197
198	/* set KIU */
199	vrkiu_write(chip, KIURST, 1);   /* reset */
200	vrkiu_write(chip, KIUSCANLINE, 0); /* 96keys */
201	vrkiu_write(chip, KIUWKS, 0x18a4); /* XXX: scan timing! */
202	vrkiu_write(chip, KIUWKI, 450);
203	vrkiu_write(chip, KIUSCANREP, 0x8023);
204	/* KEYEN | STPREP = 2 | ATSTP | ATSCAN */
205
206	return (0);
207}
208
209int
210vrkiu_intr(void *arg)
211{
212        struct vrkiu_softc *sc = arg;
213
214	/* When key scan finisshed, this entry is called. */
215#if 0
216	DPRINTF(("vrkiu_intr: intr=%x scan=%x\n",
217	    vrkiu_read(sc->sc_chip, KIUINT) & 7,
218	    vrkiu_read(sc->sc_chip, KIUSCANS) & KIUSCANS_SSTAT_MASK));
219#endif
220
221	/*
222	 * First, we must clear the interrupt register because
223	 * vrkiu_scan() may takes long time if a bitmap screen
224	 * scrolls up and it makes us to miss some key release
225	 * event.
226	 */
227	vrkiu_write(sc->sc_chip, KIUINT, 0x7); /* Clear all interrupt */
228
229#if 1
230	/* just return if kiu is scanning keyboard. */
231	if ((vrkiu_read(sc->sc_chip, KIUSCANS) & KIUSCANS_SSTAT_MASK) ==
232	    KIUSCANS_SSTAT_SCANNING)
233		return (0);
234#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