1 /* $NetBSD: gsckbc.c,v 1.4 2009/05/07 15:34:49 skrll Exp $ */
3 * Copyright (c) 2004 Jochen Kunz.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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
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>
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>
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
,
127 const struct pckbport_accessops gsckbc_accessops
= {
128 gsckbc_xt_translation
,
132 gsckbc_intr_establish
,
138 gsckbc_xt_translation(void *cookie
, pckbport_slot_t slot
, int on
)
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
)
152 if ((bus_space_read_1(sc
->sc_iot
, sc
->sc_ioh
, REG_STATUS
) & STAT_TBNE
)
155 bus_space_write_1(sc
->sc_iot
, sc
->sc_ioh
, REG_XMITDATA
, byte
);
161 gsckbc_poll_data1(void *cookie
, pckbport_slot_t slot
)
163 struct gsckbc_softc
*sc
= (struct gsckbc_softc
*) cookie
;
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
,
179 gsckbc_slot_enable(void *cookie
, pckbport_slot_t slot
, int on
)
181 struct gsckbc_softc
*sc
= (struct gsckbc_softc
*) cookie
;
188 gsckbc_intr_establish(void *cookie
, pckbport_slot_t slot
)
195 gsckbc_set_poll(void *cookie
, pckbport_slot_t slot
, int on
)
197 struct gsckbc_softc
*sc
= (struct gsckbc_softc
*) cookie
;
204 gsckbc_intr(void *arg
)
206 struct gsckbc_softc
*sc
= (struct gsckbc_softc
*) arg
;
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
,
219 if (sc
->sc_op
->sc_enable
!= 0)
220 pckbportintr(sc
->sc_op
->sc_pckbport
, sc
->sc_op
->sc_slot
,
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
)
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
;
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.
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
,
266 aprint_normal(": gsckbc_attach: can't map I/O space\n");
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
);
274 if (master_sc
== NULL
) {
275 aprint_normal(": can't find master device\n");
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. */
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
);
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
,
299 aprint_normal("Failed to attach console "
304 hp700_pagezero_unmap(pagezero_cookie
);
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
,