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