xref: /netbsd-src/sys/arch/hppa/gsc/gsckbc.c (revision 549aa5640fd486189db2ca05c6579e863feab712)
1 /* $NetBSD: gsckbc.c,v 1.3 2022/06/13 17:26:34 andvar Exp $ */
2 /*
3  * Copyright (c) 2004 Jochen Kunz.
4  * 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  * 3. The name of Jochen Kunz may not be used to endorse or promote
15  *    products derived from this software without specific prior
16  *    written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY JOCHEN KUNZ
19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL JOCHEN KUNZ
22  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 /*
32  * hppa GSC bus MD pckbport(9) frontend for the PS/2 ports found in LASI chips.
33  */
34 
35 #include <sys/cdefs.h>
36 __KERNEL_RCSID(0, "$NetBSD: gsckbc.c,v 1.3 2022/06/13 17:26:34 andvar Exp $");
37 
38 /* autoconfig and device stuff */
39 #include <sys/param.h>
40 #include <sys/device.h>
41 #include <sys/conf.h>
42 #include <machine/iomod.h>
43 #include <machine/autoconf.h>
44 #include <hppa/dev/cpudevs.h>
45 #include <hppa/gsc/gscbusvar.h>
46 #include <hppa/hppa/machdep.h>
47 #include "locators.h"
48 #include "ioconf.h"
49 
50 /* bus_space / bus_dma etc. */
51 #include <sys/bus.h>
52 #include <machine/intr.h>
53 
54 /* general system data and functions */
55 #include <sys/systm.h>
56 #include <sys/ioctl.h>
57 #include <sys/ioccom.h>
58 #include <sys/types.h>
59 
60 /* pckbport interface */
61 #include <dev/pckbport/pckbportvar.h>
62 
63 /* register offsets */
64 #define	REG_ID		0x0	/* R: ID register */
65 #define	REG_RESET	0x0	/* W: reset port */
66 #define	REG_RCVDATA	0x4	/* R: received data (4 byte FIFO) */
67 #define	REG_XMITDATA	0x4	/* W: data to transmit */
68 #define	REG_CONTROL	0x8	/* Control Bits */
69 #define	REG_STATUS	0xc	/* Status Bits */
70 #define	REG_SZ		0xc	/* Size of register set */
71 #define	REG_OFFSET	0x100	/* Address Offset of the two ports */
72 
73 /* ID values for REG_ID */
74 #define	ID_KBD		0	/* keyboard port */
75 #define	ID_AUX		1	/* mouse / aux port */
76 
77 /* Control Register Bits (R/W) */
78 #define	CTRL_ENBL	0x01	/* Enable */
79 #define	CTRL_LPBXR	0x02	/* Loopback Xmt/Rcv mode */
80 #define	CTRL_DIAG	0x20	/* Diagnostic mode */
81 #define	CTRL_DATDIR	0x40	/* External data line direct control */
82 #define	CTRL_CLKDIR	0x80	/* External clock line direct control */
83 
84 /* Status Register Bits (RO) */
85 #define	STAT_RBNE	0x01	/* Receive buffer not empty */
86 #define	STAT_TBNE	0x02	/* Transmit buffer not empty */
87 #define	STAT_TERR	0x04	/* Timeout Error */
88 #define	STAT_PERR	0x08	/* Parity Error */
89 #define	STAT_CMPINTR	0x10	/* Composite interrupt */
90 #define	STAT_DATSHD	0x40	/* Data line shadow */
91 #define	STAT_CLKSHD	0x80	/* Clock line shadow */
92 
93 
94 
95 /* autoconfig stuff */
96 static int gsckbc_match(device_t, cfdata_t, void *);
97 static void gsckbc_attach(device_t, device_t, void *);
98 
99 static int gsckbc_xt_translation(void *, pckbport_slot_t, int);
100 static int gsckbc_send_devcmd(void *, pckbport_slot_t, u_char);
101 static int gsckbc_poll_data1(void *, pckbport_slot_t);
102 static void gsckbc_slot_enable(void *, pckbport_slot_t, int);
103 static void gsckbc_intr_establish(void *, pckbport_slot_t);
104 static void gsckbc_set_poll(void *, pckbport_slot_t, int);
105 
106 static int gsckbc_intr(void *);
107 
108 
109 struct gsckbc_softc {
110 	device_t sc_dev;			/* general dev info */
111 	bus_space_tag_t sc_iot;			/* bus_space(9) tag */
112 	bus_space_handle_t sc_ioh;		/* bus_space(9) handle */
113 	struct gsckbc_softc *sc_op;		/* other port */
114 	void *sc_ih;				/* interrupt handle */
115 	pckbport_slot_t	sc_slot;		/* kbd or mouse / aux slot */
116 	pckbport_tag_t sc_pckbport;		/* port tag */
117 	device_t sc_child;			/* our child devices */
118 	int sc_poll;				/* if != 0 then pooling mode */
119 	int sc_enable;				/* if != 0 then enable */
120 };
121 
122 
123 CFATTACH_DECL_NEW(gsckbc, sizeof(struct gsckbc_softc), gsckbc_match, gsckbc_attach,
124 	NULL, NULL);
125 
126 
127 const struct pckbport_accessops gsckbc_accessops = {
128 	gsckbc_xt_translation,
129 	gsckbc_send_devcmd,
130 	gsckbc_poll_data1,
131 	gsckbc_slot_enable,
132 	gsckbc_intr_establish,
133 	gsckbc_set_poll
134 };
135 
136 
137 static int
gsckbc_xt_translation(void * cookie,pckbport_slot_t slot,int on)138 gsckbc_xt_translation(void *cookie, pckbport_slot_t slot, int on)
139 {
140 	return 0;
141 }
142 
143 
144 static int
gsckbc_send_devcmd(void * cookie,pckbport_slot_t slot,u_char byte)145 gsckbc_send_devcmd(void *cookie, pckbport_slot_t slot, u_char byte)
146 {
147 	struct gsckbc_softc *sc = (struct gsckbc_softc *) cookie;
148 
149 	if ((bus_space_read_1(sc->sc_iot, sc->sc_ioh, REG_STATUS) & STAT_TBNE)
150 	    != 0)
151 		DELAY(100);
152 	if ((bus_space_read_1(sc->sc_iot, sc->sc_ioh, REG_STATUS) & STAT_TBNE)
153 	    != 0)
154 		return 0;
155 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, REG_XMITDATA, byte);
156 	return 1;
157 }
158 
159 
160 static int
gsckbc_poll_data1(void * cookie,pckbport_slot_t slot)161 gsckbc_poll_data1(void *cookie, pckbport_slot_t slot)
162 {
163 	struct gsckbc_softc *sc = (struct gsckbc_softc *) cookie;
164 	int i;
165 
166 	for (i = 0; i < 1000; i++) {
167 		if ((bus_space_read_1(sc->sc_iot, sc->sc_ioh, REG_STATUS) &
168 		    STAT_RBNE) != 0 || sc->sc_poll == 0) {
169 			return bus_space_read_1(sc->sc_iot, sc->sc_ioh,
170 			    REG_RCVDATA);
171 		}
172 		DELAY(100);
173 	}
174 	return -1;
175 }
176 
177 
178 static void
gsckbc_slot_enable(void * cookie,pckbport_slot_t slot,int on)179 gsckbc_slot_enable(void *cookie, pckbport_slot_t slot, int on)
180 {
181 	struct gsckbc_softc *sc = (struct gsckbc_softc *) cookie;
182 
183 	sc->sc_enable = on;
184 }
185 
186 
187 static void
gsckbc_intr_establish(void * cookie,pckbport_slot_t slot)188 gsckbc_intr_establish(void *cookie, pckbport_slot_t slot)
189 {
190 	return;
191 }
192 
193 
194 static void
gsckbc_set_poll(void * cookie,pckbport_slot_t slot,int on)195 gsckbc_set_poll(void *cookie, pckbport_slot_t slot, int on)
196 {
197 	struct gsckbc_softc *sc = (struct gsckbc_softc *) cookie;
198 
199 	sc->sc_poll = on;
200 }
201 
202 
203 static int
gsckbc_intr(void * arg)204 gsckbc_intr(void *arg)
205 {
206 	struct gsckbc_softc *sc = (struct gsckbc_softc *) arg;
207 	int data;
208 
209 	while ((bus_space_read_1(sc->sc_iot, sc->sc_ioh, REG_STATUS)
210 	    & STAT_RBNE) != 0 && sc->sc_poll == 0) {
211 		data = bus_space_read_1(sc->sc_iot, sc->sc_ioh, REG_RCVDATA);
212 		if (sc->sc_enable != 0)
213 			pckbportintr(sc->sc_pckbport, sc->sc_slot, data);
214 	}
215 	while ((bus_space_read_1(sc->sc_op->sc_iot, sc->sc_op->sc_ioh,
216 	    REG_STATUS) & STAT_RBNE) != 0 && sc->sc_op->sc_poll == 0) {
217 		data = bus_space_read_1(sc->sc_op->sc_iot, sc->sc_op->sc_ioh,
218 		    REG_RCVDATA);
219 		if (sc->sc_op->sc_enable != 0)
220 			pckbportintr(sc->sc_op->sc_pckbport, sc->sc_op->sc_slot,
221 			    data);
222 	}
223 	return 1;
224 }
225 
226 
227 static int
gsckbc_match(device_t parent,cfdata_t match,void * aux)228 gsckbc_match(device_t parent, cfdata_t match, void *aux)
229 {
230 	struct gsc_attach_args *ga = aux;
231 
232 	if (ga->ga_type.iodc_type == HPPA_TYPE_FIO
233 	    && ga->ga_type.iodc_sv_model == HPPA_FIO_GPCIO)
234 		return(1);
235 	return(0);
236 }
237 
238 
239 static void
gsckbc_attach(device_t parent,device_t self,void * aux)240 gsckbc_attach(device_t parent, device_t self, void *aux)
241 {
242 	struct gsckbc_softc *sc = device_private(self);
243 	struct gsc_attach_args *ga = aux;
244 	static struct gsckbc_softc *master_sc;
245 	int pagezero_cookie;
246 	int i;
247 
248 	/*
249 	 * On hppa bus_space_map(9) maps whole pages. (surprise, surprise)
250 	 * The registers are within the same page so we can do only a single
251 	 * mapping for both devices. Also both devices use the same IRQ.
252 	 * Actually you can think of the two PS/2 ports to be a single
253 	 * device. The firmware lists them as individual devices in the
254 	 * firmware device tree so we keep this illusion to map the firmware
255 	 * device tree as close as possible to the kernel device tree.
256 	 * So we do one mapping and IRQ for both devices. The first device
257 	 * is called "master", gets the IRQ and the other is the "slave".
258 	 *
259 	 * Assumption: Master attaches first, gets the IRQ and has lower HPA.
260 	 */
261 	sc->sc_dev = self;
262 	sc->sc_iot = ga->ga_iot;
263 	if (ga->ga_irq >= 0) {
264 		if (bus_space_map(sc->sc_iot, ga->ga_hpa, REG_SZ + REG_OFFSET,
265 		    0, &sc->sc_ioh)) {
266 			aprint_normal(": gsckbc_attach: can't map I/O space\n");
267 			return;
268 		}
269 		aprint_debug(" (master)");
270 		sc->sc_ih = hppa_intr_establish(IPL_TTY, gsckbc_intr, sc,
271 		    ga->ga_ir, ga->ga_irq);
272 		master_sc = sc;
273 	} else {
274 		if (master_sc == NULL) {
275 			aprint_normal(": can't find master device\n");
276 			return;
277 		}
278 		sc->sc_op = master_sc;
279 		master_sc->sc_op = sc;
280 		sc->sc_ioh = sc->sc_op->sc_ioh + REG_OFFSET;
281 		aprint_debug(" (slave)");
282 	}
283 	/* We start in polling mode. */
284 	sc->sc_poll = 1;
285 	/* Reset port. */
286 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, REG_RESET, 0);
287 	/* Enable port hardware. */
288 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, REG_CONTROL, CTRL_ENBL);
289 	/* Flush RX FIFO. */
290 	for (i = 0; i < 5; i++)
291 		bus_space_read_1(sc->sc_iot, sc->sc_ioh, REG_RCVDATA);
292 	if (bus_space_read_1(sc->sc_iot, sc->sc_ioh, REG_ID) == ID_KBD) {
293 		aprint_normal(": keyboard\n");
294 		sc->sc_slot = PCKBPORT_KBD_SLOT;
295 		pagezero_cookie = hppa_pagezero_map();
296 		if ((hppa_hpa_t)PAGE0->mem_kbd.pz_hpa == ga->ga_hpa) {
297 			if (pckbport_cnattach(sc, &gsckbc_accessops,
298 			    sc->sc_slot) != 0)
299 				aprint_normal("Failed to attach console "
300 				    "keyboard!\n");
301 			else
302 				sc->sc_enable = 1;
303 		}
304 		hppa_pagezero_unmap(pagezero_cookie);
305 	} else {
306 		aprint_normal(": mouse\n");
307 		sc->sc_slot = PCKBPORT_AUX_SLOT;
308 	}
309 	sc->sc_pckbport = pckbport_attach(sc, &gsckbc_accessops);
310 	if (sc->sc_pckbport != NULL)
311 		sc->sc_child = pckbport_attach_slot(self, sc->sc_pckbport,
312 		    sc->sc_slot);
313 	sc->sc_poll = 0;
314 }
315 
316