/* $NetBSD: gsckbc.c,v 1.3 2022/06/13 17:26:34 andvar Exp $ */
/*
* Copyright (c) 2004 Jochen Kunz.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of Jochen Kunz may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY JOCHEN KUNZ
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JOCHEN KUNZ
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* hppa GSC bus MD pckbport(9) frontend for the PS/2 ports found in LASI chips.
*/
struct gsckbc_softc {
device_t sc_dev; /* general dev info */
bus_space_tag_t sc_iot; /* bus_space(9) tag */
bus_space_handle_t sc_ioh; /* bus_space(9) handle */
struct gsckbc_softc *sc_op; /* other port */
void *sc_ih; /* interrupt handle */
pckbport_slot_t sc_slot; /* kbd or mouse / aux slot */
pckbport_tag_t sc_pckbport; /* port tag */
device_t sc_child; /* our child devices */
int sc_poll; /* if != 0 then pooling mode */
int sc_enable; /* if != 0 then enable */
};
/*
* On hppa bus_space_map(9) maps whole pages. (surprise, surprise)
* The registers are within the same page so we can do only a single
* mapping for both devices. Also both devices use the same IRQ.
* Actually you can think of the two PS/2 ports to be a single
* device. The firmware lists them as individual devices in the
* firmware device tree so we keep this illusion to map the firmware
* device tree as close as possible to the kernel device tree.
* So we do one mapping and IRQ for both devices. The first device
* is called "master", gets the IRQ and the other is the "slave".
*
* Assumption: Master attaches first, gets the IRQ and has lower HPA.
*/
sc->sc_dev = self;
sc->sc_iot = ga->ga_iot;
if (ga->ga_irq >= 0) {
if (bus_space_map(sc->sc_iot, ga->ga_hpa, REG_SZ + REG_OFFSET,
0, &sc->sc_ioh)) {
aprint_normal(": gsckbc_attach: can't map I/O space\n");
return;
}
aprint_debug(" (master)");
sc->sc_ih = hppa_intr_establish(IPL_TTY, gsckbc_intr, sc,
ga->ga_ir, ga->ga_irq);
master_sc = sc;
} else {
if (master_sc == NULL) {
aprint_normal(": can't find master device\n");
return;
}
sc->sc_op = master_sc;
master_sc->sc_op = sc;
sc->sc_ioh = sc->sc_op->sc_ioh + REG_OFFSET;
aprint_debug(" (slave)");
}
/* We start in polling mode. */
sc->sc_poll = 1;
/* Reset port. */
bus_space_write_1(sc->sc_iot, sc->sc_ioh, REG_RESET, 0);
/* Enable port hardware. */
bus_space_write_1(sc->sc_iot, sc->sc_ioh, REG_CONTROL, CTRL_ENBL);
/* Flush RX FIFO. */
for (i = 0; i < 5; i++)
bus_space_read_1(sc->sc_iot, sc->sc_ioh, REG_RCVDATA);
if (bus_space_read_1(sc->sc_iot, sc->sc_ioh, REG_ID) == ID_KBD) {
aprint_normal(": keyboard\n");
sc->sc_slot = PCKBPORT_KBD_SLOT;
pagezero_cookie = hppa_pagezero_map();
if ((hppa_hpa_t)PAGE0->mem_kbd.pz_hpa == ga->ga_hpa) {
if (pckbport_cnattach(sc, &gsckbc_accessops,
sc->sc_slot) != 0)
aprint_normal("Failed to attach console "
"keyboard!\n");
else
sc->sc_enable = 1;
}
hppa_pagezero_unmap(pagezero_cookie);
} else {
aprint_normal(": mouse\n");
sc->sc_slot = PCKBPORT_AUX_SLOT;
}
sc->sc_pckbport = pckbport_attach(sc, &gsckbc_accessops);
if (sc->sc_pckbport != NULL)
sc->sc_child = pckbport_attach_slot(self, sc->sc_pckbport,
sc->sc_slot);
sc->sc_poll = 0;
}