NetBSD/sys/arch/hpcmips/vr/vr4181giu.c

432 lines
12 KiB
C

/* $NetBSD: vr4181giu.c,v 1.3 2005/12/11 12:17:34 christos Exp $ */
/*-
* Copyright (c) 1999-2001
* Shin Takemura and PocketBSD Project. 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the PocketBSD project
* and its contributors.
* 4. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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: vr4181giu.c,v 1.3 2005/12/11 12:17:34 christos Exp $");
#include <sys/param.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/queue.h>
#include <sys/systm.h>
#include <machine/bus.h>
#include <hpcmips/vr/vripif.h>
#include <hpcmips/vr/vr4181giureg.h>
#define MAX_GIU4181INTR 16
struct vr4181giu_intr_entry {
int ih_port;
int (*ih_fun)(void *);
void *ih_arg;
TAILQ_ENTRY(vr4181giu_intr_entry) ih_link;
};
struct vr4181giu_softc {
struct device sc_dev;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
vrip_chipset_tag_t sc_vc;
void *sc_ih;
u_int32_t sc_intr_mode[MAX_GIU4181INTR];
TAILQ_HEAD(, vr4181giu_intr_entry)
sc_intr_head[MAX_GIU4181INTR];
struct hpcio_chip sc_iochip;
struct hpcio_attach_args sc_haa;
};
static int vr4181giu_match(struct device *, struct cfdata *, void *);
static void vr4181giu_attach(struct device *, struct device *, void *);
static void vr4181giu_callback(struct device *self);
static int vr4181giu_print(void *aux, const char *pnp);
static int vr4181giu_port_read(hpcio_chip_t hc, int port);
static void vr4181giu_port_write(hpcio_chip_t hc, int port, int onoff);
static void vr4181giu_update(hpcio_chip_t hc);
static void vr4181giu_dump(hpcio_chip_t hc);
static hpcio_chip_t vr4181giu_getchip(void* scx, int chipid);
static void *vr4181giu_intr_establish(hpcio_chip_t, int, int,
int (*)(void *),void *);
static void vr4181giu_intr_disestablish(hpcio_chip_t hc, void *arg);
static void vr4181giu_intr_clear(hpcio_chip_t hc, void *arg);
static void vr4181giu_register_iochip(hpcio_chip_t hc, hpcio_chip_t iochip);
static int vr4181giu_intr(void *arg);
static struct hpcio_chip vr4181giu_iochip = {
.hc_portread = vr4181giu_port_read,
.hc_portwrite = vr4181giu_port_write,
.hc_intr_establish = vr4181giu_intr_establish,
.hc_intr_disestablish = vr4181giu_intr_disestablish,
.hc_intr_clear = vr4181giu_intr_clear,
.hc_register_iochip = vr4181giu_register_iochip,
.hc_update = vr4181giu_update,
.hc_dump = vr4181giu_dump,
};
CFATTACH_DECL(vr4181giu, sizeof(struct vr4181giu_softc),
vr4181giu_match, vr4181giu_attach, NULL, NULL);
static int
vr4181giu_match(struct device *parent, struct cfdata *match, void *aux)
{
return (2); /* 1st attach group of vrip */
}
static void
vr4181giu_attach(struct device *parent, struct device *self, void *aux)
{
struct vr4181giu_softc *sc = (struct vr4181giu_softc*) self;
struct vrip_attach_args *va = aux;
int i;
sc->sc_iot = va->va_iot;
sc->sc_vc = va->va_vc;
if (bus_space_map(sc->sc_iot, va->va_addr, va->va_size,
0 /* no cache */, &sc->sc_ioh)) {
printf(": can't map i/o space\n");
return;
}
for (i = 0; i < MAX_GIU4181INTR; i++)
TAILQ_INIT(&sc->sc_intr_head[i]);
if (!(sc->sc_ih
= vrip_intr_establish(va->va_vc, va->va_unit, 0,
IPL_BIO, vr4181giu_intr, sc))) {
printf("%s: can't establish interrupt\n", sc->sc_dev.dv_xname);
return;
}
/*
* fill hpcio_chip structure
*/
sc->sc_iochip = vr4181giu_iochip; /* structure copy */
sc->sc_iochip.hc_chipid = VRIP_IOCHIP_VR4181GIU;
sc->sc_iochip.hc_name = sc->sc_dev.dv_xname;
sc->sc_iochip.hc_sc = sc;
/* Register functions to upper interface */
vrip_register_gpio(va->va_vc, &sc->sc_iochip);
printf("\n");
/*
* hpcio I/F
*/
sc->sc_haa.haa_busname = HPCIO_BUSNAME;
sc->sc_haa.haa_sc = sc;
sc->sc_haa.haa_getchip = vr4181giu_getchip;
sc->sc_haa.haa_iot = sc->sc_iot;
while (config_found(self, &sc->sc_haa, vr4181giu_print)) ;
/*
* GIU-ISA bridge
*/
#if 1 /* XXX Sometimes mounting root device failed. Why? XXX*/
config_defer(self, vr4181giu_callback);
#else
vr4181giu_callback(self);
#endif
}
static void
vr4181giu_callback(struct device *self)
{
struct vr4181giu_softc *sc = (void *) self;
sc->sc_haa.haa_busname = "vrisab";
config_found(self, &sc->sc_haa, vr4181giu_print);
}
static int
vr4181giu_print(void *aux, const char *pnp)
{
if (pnp)
return (QUIET);
return (UNCONF);
}
static int
vr4181giu_port_read(hpcio_chip_t hc, int port)
{
struct vr4181giu_softc *sc = hc->hc_sc;
u_int16_t r;
if (port < 0 || 32 <= port)
panic("vr4181giu_port_read: invalid gpio port");
if (port < 16) {
r = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
VR4181GIU_PIOD_L_REG_W)
& 1 << port;
} else {
r = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
VR4181GIU_PIOD_H_REG_W)
& 1 << (port - 16);
}
return r ? 1 : 0;
}
static void
vr4181giu_port_write(hpcio_chip_t hc, int port, int onoff)
{
struct vr4181giu_softc *sc = hc->hc_sc;
u_int16_t r;
if (port < 16) {
r = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
VR4181GIU_PIOD_L_REG_W);
if (onoff) {
r |= 1 << port;
} else {
r &= ~(1 << port);
}
bus_space_write_2(sc->sc_iot, sc->sc_ioh,
VR4181GIU_PIOD_L_REG_W, r);
} else {
r = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
VR4181GIU_PIOD_H_REG_W);
if (onoff) {
r |= 1 << (port - 16);
} else {
r &= ~(1 << (port - 16));
}
bus_space_write_2(sc->sc_iot, sc->sc_ioh,
VR4181GIU_PIOD_H_REG_W, r);
}
}
/*
* XXXXXXXXXXXXXXXXXXXXXXXX
*/
static void
vr4181giu_update(hpcio_chip_t hc)
{
}
static void
vr4181giu_dump(hpcio_chip_t hc)
{
}
static hpcio_chip_t
vr4181giu_getchip(void* scx, int chipid)
{
struct vr4181giu_softc *sc = scx;
return (&sc->sc_iochip);
}
static void *
vr4181giu_intr_establish(
hpcio_chip_t hc,
int port, /* GPIO pin # */
int mode, /* GIU trigger setting */
int (*ih_fun)(void *),
void *ih_arg)
{
struct vr4181giu_softc *sc = hc->hc_sc;
struct vr4181giu_intr_entry *ih;
int s;
u_int32_t mask;
u_int32_t raw_intr_type;
int regmod;
int reghl;
int bitoff;
u_int16_t r;
/*
* trigger mode translation
*
* VR4181 only support for four type of interrupt trigger
* listed below:
*
* 1. high level
* 2. low level
* 3. rising edge
* 4. falling edge
*
* argument mode is a bitmap as following:
*
* 001 detection trigger (1:edge/0:level )
* 010 signal hold/through (1:hold/0:through)
* 100 detection level (1:high/0:low )
*
* possible mode value is 000B to 111B.
*
* 000 HPCIO_INTR_LEVEL_LOW_THROUGH
* 001 HPCIO_INTR_EDGE_THROUGH
* 010 HPCIO_INTR_LEVEL_LOW_HOLD
* 011 HPCIO_INTR_EDGE_HOLD
* 100 HPCIO_INTR_LEVEL_HIGH_THROUGH
* 101 falling edge and through?
* 110 HPCIO_INTR_LEVEL_HIGH_HOLD
* 111 falling edge and hold?
*/
static u_int32_t intr_mode_trans[8] = {
VR4181GIU_INTTYP_LOW_LEVEL, /* 000 */
VR4181GIU_INTTYP_RISING_EDGE, /* 001 */
VR4181GIU_INTTYP_LOW_LEVEL, /* 010 */
VR4181GIU_INTTYP_RISING_EDGE, /* 011 */
VR4181GIU_INTTYP_HIGH_LEVEL, /* 100 */
VR4181GIU_INTTYP_FALLING_EDGE, /* 101 */
VR4181GIU_INTTYP_HIGH_LEVEL, /* 110 */
VR4181GIU_INTTYP_FALLING_EDGE, /* 111 */
};
raw_intr_type = intr_mode_trans[mode];
if (raw_intr_type == VR4181GIU_INTTYP_INVALID)
panic("vr4181giu_intr_establish: invalid interrupt mode.");
if (port < 0 || MAX_GIU4181INTR <= port)
panic("vr4181giu_intr_establish: invalid interrupt line.");
if (!TAILQ_EMPTY(&sc->sc_intr_head[port])
&& raw_intr_type != sc->sc_intr_mode[port])
panic("vr4181giu_intr_establish: "
"cannot use one line with two modes at a time.");
else
sc->sc_intr_mode[port] = raw_intr_type;
mask = (1 << port);
s = splhigh();
if ((ih = malloc(sizeof *ih, M_DEVBUF, M_NOWAIT)) == NULL)
panic("vr4181giu_intr_establish: memory exhausted.");
ih->ih_port = port;
ih->ih_fun = ih_fun;
ih->ih_arg = ih_arg;
TAILQ_INSERT_TAIL(&sc->sc_intr_head[port], ih, ih_link);
/*
* setup GIU registers
*/
/* disable interrupt at first */
r = bus_space_read_2(sc->sc_iot, sc->sc_ioh, VR4181GIU_INTEN_REG_W);
r &= ~mask;
bus_space_write_2(sc->sc_iot, sc->sc_ioh, VR4181GIU_INTEN_REG_W, r);
/* mode */
regmod = port >> 3;
bitoff = (port & 0x7) << 1;
r = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
VR4181GIU_MODE0_REG_W + regmod);
r &= ~(0x3 << bitoff);
r |= (VR4181GIU_MODE_IN | VR4181GIU_MODE_GPIO) << bitoff;
bus_space_write_2(sc->sc_iot, sc->sc_ioh,
VR4181GIU_MODE0_REG_W + regmod, r);
/* interrupt type */
reghl = port < 8 ? 2 : 0; /* high byte: 0x0, lowbyte: 0x2 */
r = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
VR4181GIU_INTTYP_REG + reghl);
r &= ~(0x3 << bitoff);
r |= raw_intr_type << bitoff;
bus_space_write_2(sc->sc_iot, sc->sc_ioh,
VR4181GIU_INTTYP_REG + reghl, r);
/* clear status */
bus_space_write_2(sc->sc_iot, sc->sc_ioh,
VR4181GIU_INTSTAT_REG_W, mask);
/* unmask */
r = bus_space_read_2(sc->sc_iot, sc->sc_ioh, VR4181GIU_INTMASK_REG_W);
r &= ~mask;
bus_space_write_2(sc->sc_iot, sc->sc_ioh, VR4181GIU_INTMASK_REG_W, r);
/* enable */
r = bus_space_read_2(sc->sc_iot, sc->sc_ioh, VR4181GIU_INTEN_REG_W);
r |= mask;
bus_space_write_2(sc->sc_iot, sc->sc_ioh, VR4181GIU_INTEN_REG_W, r);
splx(s);
return ih;
}
static void
vr4181giu_intr_disestablish(hpcio_chip_t hc, void *arg)
{
}
static void
vr4181giu_intr_clear(hpcio_chip_t hc, void *arg)
{
struct vr4181giu_softc *sc = hc->hc_sc;
struct vr4181giu_intr_entry *ih = arg;
bus_space_write_2(sc->sc_iot, sc->sc_ioh,
VR4181GIU_INTSTAT_REG_W, 1 << ih->ih_port);
}
static void
vr4181giu_register_iochip(hpcio_chip_t hc, hpcio_chip_t iochip)
{
struct vr4181giu_softc *sc = hc->hc_sc;
vrip_register_gpio(sc->sc_vc, iochip);
}
/*
* interrupt handler
*/
static int
vr4181giu_intr(void *arg)
{
struct vr4181giu_softc *sc = arg;
int i;
u_int16_t r;
r = bus_space_read_2(sc->sc_iot, sc->sc_ioh, VR4181GIU_INTSTAT_REG_W);
bus_space_write_2(sc->sc_iot, sc->sc_ioh, VR4181GIU_INTSTAT_REG_W, r);
for (i = 0; i < MAX_GIU4181INTR; i++) {
if (r & (1 << i)) {
struct vr4181giu_intr_entry *ih;
TAILQ_FOREACH(ih, &sc->sc_intr_head[i], ih_link) {
ih->ih_fun(ih->ih_arg);
}
}
}
return 0;
}