Sync usage with man page.
[netbsd-mini2440.git] / sys / arch / hp700 / gsc / gsckbc.c
blob577a4021a31c44364c3fbdd76a8c807c4f7eb6bb
1 /* $NetBSD: gsckbc.c,v 1.4 2009/05/07 15:34:49 skrll Exp $ */
2 /*
3 * Copyright (c) 2004 Jochen Kunz.
4 * All rights reserved.
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.
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.
32 * hp700 GSC bus MD pckbport(9) frontend for the PS/2 ports found in LASI chips.
35 #include <sys/cdefs.h>
36 __KERNEL_RCSID(0, "$NetBSD: gsckbc.c,v 1.4 2009/05/07 15:34:49 skrll Exp $");
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 <hp700/dev/cpudevs.h>
45 #include <hp700/gsc/gscbusvar.h>
46 #include <hp700/hp700/machdep.h>
47 #include "locators.h"
48 #include "ioconf.h"
50 /* bus_space / bus_dma etc. */
51 #include <machine/bus.h>
52 #include <machine/intr.h>
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>
60 /* pckbport interface */
61 #include <dev/pckbport/pckbportvar.h>
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 /* Controll 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 */
73 /* ID values for REG_ID */
74 #define ID_KBD 0 /* keyboard port */
75 #define ID_AUX 1 /* mouse / aux port */
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 */
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 */
95 /* autoconfig stuff */
96 static int gsckbc_match(device_t, cfdata_t, void *);
97 static void gsckbc_attach(device_t, device_t, void *);
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);
106 static int gsckbc_intr(void *);
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 */
123 CFATTACH_DECL_NEW(gsckbc, sizeof(struct gsckbc_softc), gsckbc_match, gsckbc_attach,
124 NULL, NULL);
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
137 static int
138 gsckbc_xt_translation(void *cookie, pckbport_slot_t slot, int on)
140 return 0;
144 static int
145 gsckbc_send_devcmd(void *cookie, pckbport_slot_t slot, u_char byte)
147 struct gsckbc_softc *sc = (struct gsckbc_softc *) cookie;
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;
160 static int
161 gsckbc_poll_data1(void *cookie, pckbport_slot_t slot)
163 struct gsckbc_softc *sc = (struct gsckbc_softc *) cookie;
164 int i;
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);
172 DELAY(100);
174 return -1;
178 static void
179 gsckbc_slot_enable(void *cookie, pckbport_slot_t slot, int on)
181 struct gsckbc_softc *sc = (struct gsckbc_softc *) cookie;
183 sc->sc_enable = on;
187 static void
188 gsckbc_intr_establish(void *cookie, pckbport_slot_t slot)
190 return;
194 static void
195 gsckbc_set_poll(void *cookie, pckbport_slot_t slot, int on)
197 struct gsckbc_softc *sc = (struct gsckbc_softc *) cookie;
199 sc->sc_poll = on;
203 static int
204 gsckbc_intr(void *arg)
206 struct gsckbc_softc *sc = (struct gsckbc_softc *) arg;
207 int data;
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);
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);
223 return 1;
227 static int
228 gsckbc_match(device_t parent, cfdata_t match, void *aux)
230 struct gsc_attach_args *ga = aux;
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);
239 static void
240 gsckbc_attach(device_t parent, device_t self, void *aux)
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;
249 * On hp700 bus_space_map(9) mapes 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 caled "master", gets the IRQ and the other is the "slave".
259 * Assumption: Master attaches first, gets the IRQ and has lower HPA.
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;
269 aprint_debug(" (master)");
270 sc->sc_ih = hp700_intr_establish(sc->sc_dev, IPL_TTY,
271 gsckbc_intr, sc, ga->ga_int_reg, 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;
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)");
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 = hp700_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;
304 hp700_pagezero_unmap(pagezero_cookie);
305 } else {
306 aprint_normal(": mouse\n");
307 sc->sc_slot = PCKBPORT_AUX_SLOT;
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;