Files
netbsd/sys/arch/evbppc/virtex/dev/pstwo.c
2014-01-15 10:53:42 +01:00

262 lines
6.6 KiB
C

/* $NetBSD: pstwo.c,v 1.5 2012/10/27 17:17:51 chs Exp $ */
/*
* Copyright (c) 2006 Jachym Holecek
* All rights reserved.
*
* Written for DFC Design, s.r.o.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: pstwo.c,v 1.5 2012/10/27 17:17:51 chs Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/time.h>
#include <sys/syslog.h>
#include <machine/intr.h>
#include <sys/bus.h>
#include <dev/pckbport/pckbportvar.h>
#include <evbppc/virtex/virtex.h>
#include <evbppc/virtex/dev/xcvbusvar.h>
#include <evbppc/virtex/dev/pstworeg.h>
#define PSTWO_RXBUF_SIZE 32
#define NEXT(idx) (void)((idx) = ((idx) + 1) % PSTWO_RXBUF_SIZE)
struct pstwo_softc {
device_t sc_dev;
void *sc_ih;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
pckbport_tag_t sc_pt;
pckbport_slot_t sc_slot;
};
static int pstwo_intr(void *);
static void pstwo_reset(bus_space_tag_t, bus_space_handle_t, bool);
#if 0
static void pstwo_printreg(struct pstwo_softc *);
#endif
/* Interface to pckbport. */
static int pstwo_xt_translation(void *, pckbport_slot_t, int);
static int pstwo_send_devcmd(void *, pckbport_slot_t, u_char);
static int pstwo_poll_data1(void *, pckbport_slot_t);
static void pstwo_slot_enable(void *, pckbport_slot_t, int);
static void pstwo_intr_establish(void *, pckbport_slot_t);
static void pstwo_set_poll(void *, pckbport_slot_t, int);
/* Generic device. */
static void pstwo_attach(device_t, device_t, void *);
CFATTACH_DECL_NEW(pstwo, sizeof(struct pstwo_softc),
xcvbus_child_match, pstwo_attach, NULL, NULL);
static struct pckbport_accessops pstwo_ops = {
.t_xt_translation = pstwo_xt_translation,
.t_send_devcmd = pstwo_send_devcmd,
.t_poll_data1 = pstwo_poll_data1,
.t_slot_enable = pstwo_slot_enable,
.t_intr_establish = pstwo_intr_establish,
.t_set_poll = pstwo_set_poll,
};
static void
pstwo_attach(device_t parent, device_t self, void *aux)
{
struct xcvbus_attach_args *vaa = aux;
struct pstwo_softc *sc = device_private(self);
int i;
aprint_normal(": PS2 port\n");
if ((sc->sc_ih = intr_establish(vaa->vaa_intr, IST_LEVEL, IPL_TTY,
pstwo_intr, sc)) == NULL) {
aprint_error_dev(self, "could not establish interrupt\n");
return;
}
sc->sc_dev = self;
sc->sc_iot = vaa->vaa_iot;
if (bus_space_map(vaa->vaa_iot, vaa->vaa_addr, PSTWO_SIZE, 0,
&sc->sc_ioh) != 0) {
aprint_error_dev(self, "could not map registers\n");
return ;
}
pstwo_reset(sc->sc_iot, sc->sc_ioh, 1);
sc->sc_pt = pckbport_attach(sc, &pstwo_ops);
/* Emulate the concept of "slot" to make pms(4)/pckbd(4) happy. */
for (i = 0; i < PCKBPORT_NSLOTS; i++)
if (pckbport_attach_slot(self, sc->sc_pt, i) != NULL) {
sc->sc_slot = i;
break; /* only one device allowed */
}
}
#if 0
static void
pstwo_printreg(struct pstwo_softc *sc)
{
#define PRINTREG(name, reg) \
printf("%s: [0x%08x] %s -> 0x%08x\n", device_xname(sc->sc_dev), \
reg, name, bus_space_read_4(sc->sc_iot, sc->sc_ioh, reg))
PRINTREG("status ", PSTWO_STAT);
PRINTREG("recv byte", PSTWO_RX_DATA);
PRINTREG("intr stat", PSTWO_INTR_STAT);
PRINTREG("intr mask", PSTWO_INTR_MSET);
#undef PRINTREG
}
#endif
static int
pstwo_intr(void *arg)
{
struct pstwo_softc *sc = arg;
uint32_t stat;
stat = bus_space_read_4(sc->sc_iot, sc->sc_ioh, PSTWO_INTR_STAT);
bus_space_write_4(sc->sc_iot, sc->sc_ioh, PSTWO_INTR_ACK, stat);
if (stat & INTR_RX_FULL) {
uint32_t val;
val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, PSTWO_RX_DATA);
pckbportintr(sc->sc_pt, sc->sc_slot, DATA_RECV(val));
}
return (0);
}
static void
pstwo_reset(bus_space_tag_t iot, bus_space_handle_t ioh, bool restart)
{
bus_space_write_4(iot, ioh, PSTWO_CTRL, CTRL_RESET);
delay(10);
if (restart) {
bus_space_write_4(iot, ioh, PSTWO_CTRL, ~CTRL_RESET);
bus_space_write_4(iot, ioh, PSTWO_INTR_MSET, INTR_ANY);
}
}
/*
* pstwo_wait:
*
* Wait while status bits indicated by ${mask} have the value ${set}.
* Return zero on success, one on timeout.
*/
static int
pstwo_wait(struct pstwo_softc *sc, uint32_t mask, bool set)
{
uint32_t val = (set ? mask : 0);
int i = 1000;
while (i--) {
if ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, PSTWO_STAT) &
mask) == val) {
delay(10);
continue;
}
return (0);
}
return (1);
}
/*
* pckbport
*/
static int
pstwo_xt_translation(void *arg, pckbport_slot_t port, int enable)
{
/* Can't do translation, so disable succeeds & enable fails. */
return (!enable);
}
static int
pstwo_send_devcmd(void *arg, pckbport_slot_t slot, u_char byte)
{
struct pstwo_softc *sc = arg;
if (pstwo_wait(sc, STAT_TX_BUSY, 1))
return (0);
bus_space_write_4(sc->sc_iot, sc->sc_ioh, PSTWO_TX_DATA,
DATA_SEND(byte));
return (1);
}
static int
pstwo_poll_data1(void *arg, pckbport_slot_t slot)
{
struct pstwo_softc *sc = arg;
uint32_t val;
if (pstwo_wait(sc, STAT_RX_DONE, 0))
return (-1);
val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, PSTWO_RX_DATA);
return DATA_RECV(val);
}
static void
pstwo_slot_enable(void *arg, pckbport_slot_t slot, int enable)
{
/* Nothing to do */
}
static void
pstwo_intr_establish(void *arg, pckbport_slot_t slot)
{
/* XXX no-op because we don't support polled mode */
}
static void
pstwo_set_poll(void *arg, pckbport_slot_t slot, int enable)
{
/* XXX only needed by console */
}