Home | History | Annotate | Line # | Download | only in gsc
      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
    138 gsckbc_xt_translation(void *cookie, pckbport_slot_t slot, int on)
    139 {
    140 	return 0;
    141 }
    142 
    143 
    144 static int
    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
    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
    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
    188 gsckbc_intr_establish(void *cookie, pckbport_slot_t slot)
    189 {
    190 	return;
    191 }
    192 
    193 
    194 static void
    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
    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
    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
    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