/* $NetBSD: vrgiu.c,v 1.10 2000/04/03 03:40:00 sato Exp $ */ /*- * Copyright (c) 1999 * 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 #include #include #include #include #define TAILQ_FOREACH(var, head, field) \ for (var = TAILQ_FIRST(head); var; var = TAILQ_NEXT(var, field)) #define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) #include #include #include /* hpcmips_verbose */ #include #include #include #include "locators.h" #define VRGIUDEBUG #ifdef VRGIUDEBUG #define DEBUG_IO 1 #define DEBUG_INTR 2 #ifndef VRGIUDEBUG_CONF #define VRGIUDEBUG_CONF 0 #endif /* VRGIUDEBUG_CONF */ int vrgiu_debug = VRGIUDEBUG_CONF; #define DPRINTF(flag, arg) if (vrgiu_debug & flag) printf arg; #define DDUMP_IO(flag, sc) if (vrgiu_debug & flag) vrgiu_dump_io(sc); #define DDUMP_IOSETTING(flag, sc) \ if (vrgiu_debug & flag) vrgiu_dump_iosetting(sc); #define VPRINTF(flag, arg) \ if (hpcmips_verbose || vrgiu_debug & flag) printf arg; #define VDUMP_IO(flag, sc) \ if (hpcmips_verbose || vrgiu_debug & flag) vrgiu_dump_io(sc); #define VDUMP_IOSETTING(flag, sc) \ if (hpcmips_verbose || vrgiu_debug & flag) vrgiu_dump_iosetting(sc); #else #define DPRINTF(flag, arg) #define DDUMP_IO(flag, sc) #define DDUMP_IOSETTING(flag, sc) #define VPRINTF(flag, arg) if (hpcmips_verbose) printf arg; #define VDUMP_IO(flag, sc) if (hpcmips_verbose) vrgiu_dump_io(sc); #define VDUMP_IOSETTING(flag, sc) \ if (hpcmips_verbose) vrgiu_dump_iosetting(sc); #endif #define LEGAL_INTR_PORT(x) ((x) >= 0 && (x) < MAX_GPIO_INOUT) #define LEGAL_OUT_PORT(x) ((x) >= 0 && (x) < MAX_GPIO_OUT) int vrgiu_match __P((struct device*, struct cfdata*, void*)); void vrgiu_attach __P((struct device*, struct device*, void*)); int vrgiu_intr __P((void*)); int vrgiu_print __P((void*, const char*)); void vrgiu_callback __P((struct device*)); void vrgiu_dump_regs(struct vrgiu_softc *sc); void vrgiu_dump_io(struct vrgiu_softc *sc); void vrgiu_dump_iosetting(struct vrgiu_softc *sc); u_int32_t vrgiu_regread_4 __P((vrgiu_chipset_tag_t, bus_addr_t)); u_int16_t vrgiu_regread __P((vrgiu_chipset_tag_t, bus_addr_t)); void vrgiu_regwrite_4 __P((vrgiu_chipset_tag_t, bus_addr_t, u_int32_t)); void vrgiu_regwrite __P((vrgiu_chipset_tag_t, bus_addr_t, u_int16_t)); int vrgiu_port_read __P((vrgiu_chipset_tag_t, int)); int vrgiu_port_write __P((vrgiu_chipset_tag_t, int, int)); void *vrgiu_intr_establish __P((vrgiu_chipset_tag_t, int, int, int, int (*)(void *), void*)); void vrgiu_intr_disestablish __P((vrgiu_chipset_tag_t, void*)); struct vrgiu_function_tag vrgiu_functions = { vrgiu_port_read, vrgiu_port_write, vrgiu_regread_4, vrgiu_regwrite_4, vrgiu_intr_establish, vrgiu_intr_disestablish }; struct cfattach vrgiu_ca = { sizeof(struct vrgiu_softc), vrgiu_match, vrgiu_attach }; int vrgiu_match(parent, cf, aux) struct device *parent; struct cfdata *cf; void *aux; { return 2; /* 1st attach group of vrip */ } void vrgiu_attach(parent, self, aux) struct device *parent; struct device *self; void *aux; { struct vrip_attach_args *va = aux; struct vrgiu_softc *sc = (void*)self; struct gpbus_attach_args gpa; int i; sc->sc_vc = va->va_vc; sc->sc_iot = va->va_iot; bus_space_map(sc->sc_iot, va->va_addr, va->va_size, 0 /* no cache */, &sc->sc_ioh); /* * Disable all interrupts. */ sc->sc_intr_mask = 0; printf("\n"); #ifdef WINCE_DEFAULT_SETTING #warning WINCE_DEFAULT_SETTING #else VPRINTF(DEBUG_IO, ("WIN setting: ")); VDUMP_IOSETTING(DEBUG_IO, sc); VPRINTF(DEBUG_IO, ("\n")); vrgiu_regwrite_4(sc, GIUINTEN_REG, sc->sc_intr_mask); #endif for (i = 0; i < MAX_GPIO_INOUT; i++) TAILQ_INIT(&sc->sc_intr_head[i]); if (!(sc->sc_ih = vrip_intr_establish(va->va_vc, va->va_intr, IPL_BIO, vrgiu_intr, sc))) { printf("%s: can't establish interrupt\n", sc->sc_dev.dv_xname); return; } vrgiu_functions.gf_intr_establish = vrgiu_intr_establish; vrgiu_functions.gf_intr_disestablish = vrgiu_intr_disestablish; /* * Register functions to upper interface. */ vrip_giu_function_register(va->va_vc, &vrgiu_functions, self); /* Display port status (Input/Output) for debugging */ VPRINTF(DEBUG_IO, ("I/O setting: ")); DDUMP_IOSETTING(DEBUG_IO, sc); VPRINTF(DEBUG_IO, ("\n")); VPRINTF(DEBUG_IO, (" data:")); VDUMP_IO(DEBUG_IO, sc); /* * General purpose bus */ gpa.gpa_busname = "gpbus"; gpa.gpa_gc = sc; gpa.gpa_gf = &vrgiu_functions; while (config_found(self, &gpa, vrgiu_print)) ; /* * GIU-ISA bridge */ #if 1 /* XXX Sometimes mounting root device failed. Why? XXX*/ config_defer(self, vrgiu_callback); #else vrgiu_callback(self); #endif } void vrgiu_callback(self) struct device *self; { struct vrgiu_softc *sc = (void*)self; struct gpbus_attach_args gpa; gpa.gpa_busname = "vrisab"; gpa.gpa_gc = sc; gpa.gpa_gf = &vrgiu_functions; config_found(self, &gpa, vrgiu_print); } int vrgiu_print(aux, pnp) void *aux; const char *pnp; { if (pnp) return (QUIET); return (UNCONF); } void vrgiu_dump_iosetting(sc) struct vrgiu_softc *sc; { long iosel, inten, useupdn, termupdn; u_int32_t m; iosel= vrgiu_regread_4(sc, GIUIOSEL_REG); inten= vrgiu_regread_4(sc, GIUINTEN_REG); useupdn = vrgiu_regread(sc, GIUUSEUPDN_REG_W); termupdn = vrgiu_regread(sc, GIUTERMUPDN_REG_W); for (m = 0x80000000; m; m >>=1) printf ("%c" , (useupdn&m) ? ((termupdn&m) ? 'U' : 'D') : ((iosel&m) ? 'o' : ((inten&m)?'I':'i'))); } void vrgiu_dump_io(sc) struct vrgiu_softc *sc; { u_int32_t preg[2]; preg[0] = vrgiu_regread_4(sc, GIUPIOD_REG); preg[1] = vrgiu_regread_4(sc, GIUPODAT_REG); bitdisp64(preg); } void vrgiu_dump_regs(sc) struct vrgiu_softc *sc; { if (sc == NULL) { panic("%s(%d): VRGIU device not initialized\n", __FILE__, __LINE__); } printf(" IOSEL: %08x\n", vrgiu_regread_4(sc, GIUIOSEL_REG)); printf(" PIOD: %08x\n", vrgiu_regread_4(sc, GIUPIOD_REG)); printf(" PODAT: %08x\n", vrgiu_regread_4(sc, GIUPODAT_REG)); printf(" INTSTAT: %08x\n", vrgiu_regread_4(sc, GIUINTSTAT_REG)); printf(" INTEN: %08x\n", vrgiu_regread_4(sc, GIUINTEN_REG)); printf(" INTTYP: %08x\n", vrgiu_regread_4(sc, GIUINTTYP_REG)); printf(" INTALSEL: %08x\n", vrgiu_regread_4(sc, GIUINTALSEL_REG)); printf(" INTHTSEL: %08x\n", vrgiu_regread_4(sc, GIUINTHTSEL_REG)); } /* * GIU regster access method. */ u_int32_t vrgiu_regread_4(vc, offs) vrgiu_chipset_tag_t vc; bus_addr_t offs; { struct vrgiu_softc *sc = (void*)vc; u_int16_t reg[2]; bus_space_read_region_2 (sc->sc_iot, sc->sc_ioh, offs, reg, 2); return reg[0]|(reg[1]<<16); } u_int16_t vrgiu_regread(vc, off) vrgiu_chipset_tag_t vc; bus_addr_t off; { struct vrgiu_softc *sc = (void*)vc; return bus_space_read_2(sc->sc_iot, sc->sc_ioh, off); } void vrgiu_regwrite_4(vc, offs, data) vrgiu_chipset_tag_t vc; bus_addr_t offs; u_int32_t data; { struct vrgiu_softc *sc = (void*)vc; u_int16_t reg[2]; reg[0] = data & 0xffff; reg[1] = (data>>16)&0xffff; bus_space_write_region_2 (sc->sc_iot, sc->sc_ioh, offs, reg, 2); } void vrgiu_regwrite(vc, off, data) vrgiu_chipset_tag_t vc; bus_addr_t off; u_int16_t data; { struct vrgiu_softc *sc = (void*)vc; bus_space_write_2(sc->sc_iot, sc->sc_ioh, off, data); } /* * PORT */ int vrgiu_port_read(vc, port) vrgiu_chipset_tag_t vc; int port; { int on; if (!LEGAL_OUT_PORT(port)) panic("vrgiu_port_read: illegal gpio port"); if (port < 32) on = (vrgiu_regread_4(vc, GIUPIOD_REG) & (1 << port)); else on = (vrgiu_regread_4(vc, GIUPODAT_REG) & (1 << (port - 32))); return (on ? 1 : 0); } int vrgiu_port_write(vc, port, onoff) vrgiu_chipset_tag_t vc; int port; int onoff; { u_int32_t reg[2]; int bank; if (!LEGAL_OUT_PORT(port)) panic("vrgiu_port_write: illegal gpio port"); reg[0] = vrgiu_regread_4(vc, GIUPIOD_REG); reg[1] = vrgiu_regread_4(vc, GIUPODAT_REG); bank = port < 32 ? 0 : 1; if (bank == 1) port -= 32; if (onoff) reg[bank] |= (1<sc_intr_mode[port] && mode != sc->sc_intr_mode[port]) panic ("vrgiu_intr_establish: bogus interrupt type."); else sc->sc_intr_mode[port] = mode; mask = (1 << port); s = splhigh(); if (!(ih = malloc(sizeof(struct vrgiu_intr_entry), M_DEVBUF, M_NOWAIT))) panic ("vrgiu_intr_establish: no memory."); 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); #ifdef WINCE_DEFAULT_SETTING #warning WINCE_DEFAULT_SETTING #else /* * Setup registers */ /* Input mode */ reg = vrgiu_regread_4(sc, GIUIOSEL_REG); reg &= ~mask; vrgiu_regwrite_4(sc, GIUIOSEL_REG, reg); /* interrupt type */ reg = vrgiu_regread_4(sc, GIUINTTYP_REG); DPRINTF(DEBUG_INTR, ("[%s->",reg & mask ? "edge" : "level")); if (mode & VRGIU_INTR_EDGE) { DPRINTF(DEBUG_INTR, ("edge]")); reg |= mask; /* edge */ } else { DPRINTF(DEBUG_INTR, ("level]")); reg &= ~mask; /* level */ } vrgiu_regwrite_4(sc, GIUINTTYP_REG, reg); /* interrupt level */ if (!(mode & VRGIU_INTR_EDGE)) { reg = vrgiu_regread_4(sc, GIUINTALSEL_REG); DPRINTF(DEBUG_INTR, ("[%s->",reg & mask ? "high" : "low")); if (mode & VRGIU_INTR_HIGH) { DPRINTF(DEBUG_INTR, ("high]")); reg |= mask; /* high */ } else { DPRINTF(DEBUG_INTR, ("low]")); reg &= ~mask; /* low */ } vrgiu_regwrite_4(sc, GIUINTALSEL_REG, reg); } /* hold or through */ reg = vrgiu_regread_4(sc, GIUINTHTSEL_REG); DPRINTF(DEBUG_INTR, ("[%s->",reg & mask ? "hold" : "through")); if (mode & VRGIU_INTR_HOLD) { DPRINTF(DEBUG_INTR, ("hold]")); reg |= mask; /* hold */ } else { DPRINTF(DEBUG_INTR, ("through]")); reg &= ~mask; /* through */ } vrgiu_regwrite_4(sc, GIUINTHTSEL_REG, reg); #endif /* * clear interrupt status */ reg = vrgiu_regread_4(sc, GIUINTSTAT_REG); reg &= ~mask; vrgiu_regwrite_4(sc, GIUINTSTAT_REG, reg); /* * enable interrupt */ #ifdef WINCE_DEFAULT_SETTING #warning WINCE_DEFAULT_SETTING #else sc->sc_intr_mask |= mask; vrgiu_regwrite_4(sc, GIUINTEN_REG, sc->sc_intr_mask); /* Unmask GIU level 2 mask register */ vrip_intr_setmask2(sc->sc_vc, sc->sc_ih, (1<ih_port; struct vrgiu_intr_entry *ih; int s; s = splhigh(); TAILQ_FOREACH(ih, &sc->sc_intr_head[port], ih_link) { if (ih == ihe) { TAILQ_REMOVE(&sc->sc_intr_head[port], ih, ih_link); free(ih, M_DEVBUF); if (TAILQ_EMPTY(&sc->sc_intr_head[port])) { /* Disable interrupt */ #ifdef WINCE_DEFAULT_SETTING #warning WINCE_DEFAULT_SETTING #else sc->sc_intr_mask &= ~(1<sc_intr_mask); #endif } splx(s); return; } } panic("vrgiu_intr_disetablish: no such a handle."); /* NOTREACHED */ } int vrgiu_intr(arg) void *arg; { #ifdef DUMP_GIU_LEVEL2_INTR #warning DUMP_GIU_LEVEL2_INTR static u_int32_t oreg; #endif struct vrgiu_softc *sc = arg; int i; u_int32_t reg; /* Get Level 2 interrupt status */ vrip_intr_get_status2 (sc->sc_vc, sc->sc_ih, ®); #ifdef DUMP_GIU_LEVEL2_INTR #warning DUMP_GIU_LEVEL2_INTR { u_int32_t uedge, dedge, j; for (j = 0x80000000; j > 0; j >>=1) printf ("%c" , reg&j ? '|' : '.'); uedge = (reg ^ oreg) & reg; dedge = (reg ^ oreg) & ~reg; if (uedge || dedge) { for (j = 0; j < 32; j++) { if (uedge & (1 << j)) printf ("+%d", j); else if (dedge & (1 << j)) printf ("-%d", j); } } oreg = reg; printf ("\n"); } #endif /* Clear interrupt */ vrgiu_regwrite_4(sc, GIUINTSTAT_REG, vrgiu_regread_4(sc, GIUINTSTAT_REG)); /* Dispatch handler */ for (i = 0; i < MAX_GPIO_INOUT; i++) { if (reg & (1 << i)) { register struct vrgiu_intr_entry *ih; TAILQ_FOREACH(ih, &sc->sc_intr_head[i], ih_link) { ih->ih_fun(ih->ih_arg); } } } return 0; }