diff options
| author | scw <scw@NetBSD.org> | 2002-02-12 20:38:43 +0000 |
|---|---|---|
| committer | scw <scw@NetBSD.org> | 2002-02-12 20:38:43 +0000 |
| commit | 08bde987bb7ce6a5fa7f4c155a4e3718b5877c20 (patch) | |
| tree | eb3b3f86030960c01c86a78f8cd66ccacf3ef185 /sys | |
| parent | acb4381e275e6a3bafb36c6803893fbe78f25e6e (diff) | |
Separate out devices common to many Motorola mvme boards (68k, 88k and ppc)
in preparation for future mvmeppc and mvme88k ports.
This needs a bit if tidying up to make it trully shareable, which will
happen as the new mvme ports are added.
Diffstat (limited to 'sys')
| -rw-r--r-- | sys/dev/mvme/if_ie_mvme.c | 352 | ||||
| -rw-r--r-- | sys/dev/mvme/if_iereg.h | 64 | ||||
| -rw-r--r-- | sys/dev/mvme/lpt_mvme.c | 396 | ||||
| -rw-r--r-- | sys/dev/mvme/lpt_pcctwo.c | 294 | ||||
| -rw-r--r-- | sys/dev/mvme/lptvar.h | 95 | ||||
| -rw-r--r-- | sys/dev/mvme/memc.c | 640 | ||||
| -rw-r--r-- | sys/dev/mvme/memcreg.h | 177 | ||||
| -rw-r--r-- | sys/dev/mvme/memcvar.h | 69 | ||||
| -rw-r--r-- | sys/dev/mvme/mvmebus.c | 961 | ||||
| -rw-r--r-- | sys/dev/mvme/mvmebus.h | 161 | ||||
| -rw-r--r-- | sys/dev/mvme/osiop_pcctwo.c | 191 | ||||
| -rw-r--r-- | sys/dev/mvme/pcctwo.c | 204 | ||||
| -rw-r--r-- | sys/dev/mvme/pcctworeg.h | 316 | ||||
| -rw-r--r-- | sys/dev/mvme/pcctwovar.h | 103 | ||||
| -rw-r--r-- | sys/dev/mvme/vme_two.c | 353 | ||||
| -rw-r--r-- | sys/dev/mvme/vme_two_isr.c | 429 | ||||
| -rw-r--r-- | sys/dev/mvme/vme_tworeg.h | 526 | ||||
| -rw-r--r-- | sys/dev/mvme/vme_twovar.h | 79 |
18 files changed, 5410 insertions, 0 deletions
diff --git a/sys/dev/mvme/if_ie_mvme.c b/sys/dev/mvme/if_ie_mvme.c new file mode 100644 index 00000000000..45e8d6e7884 --- /dev/null +++ b/sys/dev/mvme/if_ie_mvme.c @@ -0,0 +1,352 @@ +/* $NetBSD: if_ie_mvme.c,v 1.1 2002/02/12 20:38:43 scw Exp $ */ + +/*- + * Copyright (c) 1999, 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Steve C. Woodford. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/errno.h> +#include <sys/device.h> +#include <sys/protosw.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/if_ether.h> +#include <net/if_media.h> + +#include <uvm/uvm_extern.h> + +#include <machine/autoconf.h> +#include <machine/cpu.h> +#include <machine/bus.h> + +#include <dev/ic/i82586reg.h> +#include <dev/ic/i82586var.h> + +#include <dev/mvme/if_iereg.h> +#include <dev/mvme/pcctwovar.h> +#include <dev/mvme/pcctworeg.h> + + +int ie_pcctwo_match __P((struct device *, struct cfdata *, void *)); +void ie_pcctwo_attach __P((struct device *, struct device *, void *)); + +struct ie_pcctwo_softc { + struct ie_softc ps_ie; + bus_space_tag_t ps_bust; + bus_space_handle_t ps_bush; + struct evcnt ps_evcnt; +}; + +struct cfattach ie_pcctwo_ca = { + sizeof(struct ie_pcctwo_softc), ie_pcctwo_match, ie_pcctwo_attach +}; + +extern struct cfdriver ie_cd; + + +/* Functions required by the i82586 MI driver */ +static void ie_reset __P((struct ie_softc *, int)); +static int ie_intrhook __P((struct ie_softc *, int)); +static void ie_hwinit __P((struct ie_softc *)); +static void ie_atten __P((struct ie_softc *, int)); + +static void ie_copyin __P((struct ie_softc *, void *, int, size_t)); +static void ie_copyout __P((struct ie_softc *, const void *, int, size_t)); + +static u_int16_t ie_read_16 __P((struct ie_softc *, int)); +static void ie_write_16 __P((struct ie_softc *, int, u_int16_t)); +static void ie_write_24 __P((struct ie_softc *, int, int)); + +/* + * i82596 Support Routines for MVME1[67][27] and MVME187 Boards + */ +static void +ie_reset(sc, why) + struct ie_softc *sc; + int why; +{ + struct ie_pcctwo_softc *ps; + u_int32_t scp_addr; + + ps = (struct ie_pcctwo_softc *) sc; + + switch (why) { + case CHIP_PROBE: + case CARD_RESET: + bus_space_write_2(ps->ps_bust, ps->ps_bush, IE_MPUREG_UPPER, + IE_PORT_RESET); + bus_space_write_2(ps->ps_bust, ps->ps_bush, IE_MPUREG_LOWER, 0); + delay(1000); + + /* + * Set the BUSY and BUS_USE bytes here, since the MI code + * incorrectly assumes it can use byte addressing to set it. + * (due to wrong-endianess of the chip) + */ + ie_write_16(sc, IE_ISCP_BUSY(sc->iscp), 1); + ie_write_16(sc, IE_SCP_BUS_USE(sc->scp), IE_BUS_USE); + + scp_addr = sc->scp + (u_int) sc->sc_iobase; + scp_addr |= IE_PORT_ALT_SCP; + + bus_space_write_2(ps->ps_bust, ps->ps_bush, IE_MPUREG_UPPER, + scp_addr & 0xffff); + bus_space_write_2(ps->ps_bust, ps->ps_bush, IE_MPUREG_LOWER, + (scp_addr >> 16) & 0xffff); + delay(1000); + break; + } +} + +/* ARGSUSED */ +static int +ie_intrhook(sc, when) + struct ie_softc *sc; + int when; +{ + struct ie_pcctwo_softc *ps; + u_int8_t reg; + + ps = (struct ie_pcctwo_softc *) sc; + + if (when == INTR_EXIT) { + reg = pcc2_reg_read(sys_pcctwo, PCC2REG_ETH_ICSR); + reg |= PCCTWO_ICR_ICLR; + pcc2_reg_write(sys_pcctwo, PCC2REG_ETH_ICSR, reg); + } + return (0); +} + +/* ARGSUSED */ +static void +ie_hwinit(sc) + struct ie_softc *sc; +{ + u_int8_t reg; + + reg = pcc2_reg_read(sys_pcctwo, PCC2REG_ETH_ICSR); + reg |= PCCTWO_ICR_IEN | PCCTWO_ICR_ICLR; + pcc2_reg_write(sys_pcctwo, PCC2REG_ETH_ICSR, reg); +} + +/* ARGSUSED */ +static void +ie_atten(sc, reason) + struct ie_softc *sc; + int reason; +{ + struct ie_pcctwo_softc *ps; + + ps = (struct ie_pcctwo_softc *) sc; + bus_space_write_4(ps->ps_bust, ps->ps_bush, IE_MPUREG_CA, 0); +} + +static void +ie_copyin(sc, dst, offset, size) + struct ie_softc *sc; + void *dst; + int offset; + size_t size; +{ + if (size == 0) /* This *can* happen! */ + return; + +#if 0 + bus_space_read_region_1(sc->bt, sc->bh, offset, dst, size); +#else + /* A minor optimisation ;-) */ + memcpy(dst, (void *) ((u_long) sc->bh + (u_long) offset), size); +#endif +} + +static void +ie_copyout(sc, src, offset, size) + struct ie_softc *sc; + const void *src; + int offset; + size_t size; +{ + if (size == 0) /* This *can* happen! */ + return; + +#if 0 + bus_space_write_region_1(sc->bt, sc->bh, offset, src, size); +#else + /* A minor optimisation ;-) */ + memcpy((void *) ((u_long) sc->bh + (u_long) offset), src, size); +#endif +} + +static u_int16_t +ie_read_16(sc, offset) + struct ie_softc *sc; + int offset; +{ + + return (bus_space_read_2(sc->bt, sc->bh, offset)); +} + +static void +ie_write_16(sc, offset, value) + struct ie_softc *sc; + int offset; + u_int16_t value; +{ + + bus_space_write_2(sc->bt, sc->bh, offset, value); +} + +static void +ie_write_24(sc, offset, addr) + struct ie_softc *sc; + int offset; + int addr; +{ + + addr += (int) sc->sc_iobase; + + bus_space_write_2(sc->bt, sc->bh, offset, addr & 0xffff); + bus_space_write_2(sc->bt, sc->bh, offset + 2, (addr >> 16) & 0x00ff); +} + +/* ARGSUSED */ +int +ie_pcctwo_match(parent, cf, args) + struct device *parent; + struct cfdata *cf; + void *args; +{ + struct pcctwo_attach_args *pa; + + pa = args; + + if (strcmp(pa->pa_name, ie_cd.cd_name)) + return (0); + + pa->pa_ipl = cf->pcctwocf_ipl; + + return (1); +} + +/* ARGSUSED */ +void +ie_pcctwo_attach(parent, self, args) + struct device *parent; + struct device *self; + void *args; +{ + struct pcctwo_attach_args *pa; + struct ie_pcctwo_softc *ps; + struct ie_softc *sc; + bus_dma_segment_t seg; + int rseg; + + pa = (struct pcctwo_attach_args *) args; + ps = (struct ie_pcctwo_softc *) self; + sc = (struct ie_softc *) self; + + /* Map the MPU controller registers in PCCTWO space */ + ps->ps_bust = pa->pa_bust; + bus_space_map(pa->pa_bust, pa->pa_offset, IE_MPUREG_SIZE, + 0, &ps->ps_bush); + + /* Get contiguous DMA-able memory for the IE chip */ + if (bus_dmamem_alloc(pa->pa_dmat, ether_data_buff_size, NBPG, 0, + &seg, 1, &rseg, + BUS_DMA_NOWAIT | BUS_DMA_ONBOARD_RAM | BUS_DMA_24BIT) != 0) { + printf("%s: Failed to allocate ether buffer\n", self->dv_xname); + return; + } + if (bus_dmamem_map(pa->pa_dmat, &seg, rseg, ether_data_buff_size, + (caddr_t *) & sc->sc_maddr, BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) { + printf("%s: Failed to map ether buffer\n", self->dv_xname); + bus_dmamem_free(pa->pa_dmat, &seg, rseg); + return; + } + sc->bt = pa->pa_bust; + sc->bh = (bus_space_handle_t) sc->sc_maddr; /* XXXSCW Better way? */ + sc->sc_iobase = (void *) seg.ds_addr; + sc->sc_msize = ether_data_buff_size; + memset(sc->sc_maddr, 0, ether_data_buff_size); + + sc->hwreset = ie_reset; + sc->hwinit = ie_hwinit; + sc->chan_attn = ie_atten; + sc->intrhook = ie_intrhook; + sc->memcopyin = ie_copyin; + sc->memcopyout = ie_copyout; + sc->ie_bus_barrier = NULL; + sc->ie_bus_read16 = ie_read_16; + sc->ie_bus_write16 = ie_write_16; + sc->ie_bus_write24 = ie_write_24; + sc->sc_mediachange = NULL; + sc->sc_mediastatus = NULL; + + sc->scp = 0; + sc->iscp = sc->scp + ((IE_SCP_SZ + 15) & ~15); + sc->scb = sc->iscp + IE_ISCP_SZ; + sc->buf_area = sc->scb + IE_SCB_SZ; + sc->buf_area_sz = sc->sc_msize - (sc->buf_area - sc->scp); + + /* + * BUS_USE -> Interrupt Active High (edge-triggered), + * Lock function enabled, + * Internal bus throttle timer triggering, + * 82586 operating mode. + */ + ie_write_16(sc, IE_SCP_BUS_USE(sc->scp), IE_BUS_USE); + ie_write_24(sc, IE_SCP_ISCP(sc->scp), sc->iscp); + ie_write_16(sc, IE_ISCP_SCB(sc->iscp), sc->scb); + ie_write_24(sc, IE_ISCP_BASE(sc->iscp), sc->scp); + + /* This has the side-effect of resetting the chip */ + i82586_proberam(sc); + + /* Attach the MI back-end */ + i82586_attach(sc, "onboard", mvme_ea, NULL, 0, 0); + + /* Register the event counter */ + evcnt_attach_dynamic(&ps->ps_evcnt, EVCNT_TYPE_INTR, + pcctwointr_evcnt(pa->pa_ipl), "ether", sc->sc_dev.dv_xname); + + /* Finally, hook the hardware interrupt */ + pcctwointr_establish(PCCTWOV_LANC_IRQ, i82586_intr, pa->pa_ipl, sc, + &ps->ps_evcnt); +} diff --git a/sys/dev/mvme/if_iereg.h b/sys/dev/mvme/if_iereg.h new file mode 100644 index 00000000000..5a1354cc2fd --- /dev/null +++ b/sys/dev/mvme/if_iereg.h @@ -0,0 +1,64 @@ +/* $NetBSD: if_iereg.h,v 1.1 2002/02/12 20:38:43 scw Exp $ */ + +/*- + * Copyright (c) 1999, 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Steve C. Woodford. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* + * Definitions for the MPU Port and Channel Attention registers + * of the onboard i82596 Ethernet controller on MVME68K and MVME88K boards. + */ +#ifndef __mvme_if_iereg_h +#define __mvme_if_iereg_h + +#define IE_MPUREG_UPPER 0x00 /* Upper Command Word */ +#define IE_MPUREG_LOWER 0x02 /* Lower Command Word */ +#define IE_MPUREG_CA 0x04 /* Channel Attention. Dummy Rd or Wr */ + +#define IE_MPUREG_SIZE 0x08 + +/* + * BUS_USE -> Interrupt Active High (edge-triggered), + * Lock function enabled, + * Internal bus throttle timer triggering, + * 82586 operating mode. + */ +#define IE_BUS_USE (IE_SYSBUS_596_INTHIGH | \ + IE_SYSBUS_596_LOCK | \ + IE_SYSBUS_596_TRGINT | \ + IE_SYSBUS_596_82586 | \ + IE_SYSBUS_596_RSVD_SET) + +#endif /* __mvme_if_iereg_h */ diff --git a/sys/dev/mvme/lpt_mvme.c b/sys/dev/mvme/lpt_mvme.c new file mode 100644 index 00000000000..ccaaba7cec7 --- /dev/null +++ b/sys/dev/mvme/lpt_mvme.c @@ -0,0 +1,396 @@ +/* $NetBSD: lpt_mvme.c,v 1.1 2002/02/12 20:38:44 scw Exp $ */ + +/*- + * Copyright (c) 1999, 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Steve C. Woodford. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* + * Copyright (c) 1993, 1994 Charles M. Hannum. + * Copyright (c) 1990 William F. Jolitz, TeleMuse + * 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 software is a component of "386BSD" developed by + * William F. Jolitz, TeleMuse. + * 4. Neither the name of the developer nor the name "386BSD" + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS A COMPONENT OF 386BSD DEVELOPED BY WILLIAM F. JOLITZ + * AND IS INTENDED FOR RESEARCH AND EDUCATIONAL PURPOSES ONLY. THIS + * SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT. + * THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT + * NOT MAKE USE OF THIS WORK. + * + * FOR USERS WHO WISH TO UNDERSTAND THE 386BSD SYSTEM DEVELOPED + * BY WILLIAM F. JOLITZ, WE RECOMMEND THE USER STUDY WRITTEN + * REFERENCES SUCH AS THE "PORTING UNIX TO THE 386" SERIES + * (BEGINNING JANUARY 1991 "DR. DOBBS JOURNAL", USA AND BEGINNING + * JUNE 1991 "UNIX MAGAZIN", GERMANY) BY WILLIAM F. JOLITZ AND + * LYNNE GREER JOLITZ, AS WELL AS OTHER BOOKS ON UNIX AND THE + * ON-LINE 386BSD USER MANUAL BEFORE USE. A BOOK DISCUSSING THE INTERNALS + * OF 386BSD ENTITLED "386BSD FROM THE INSIDE OUT" WILL BE AVAILABLE LATE 1992. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``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 DEVELOPER 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. + */ + +/* + * Device Driver for an MVME68K/MVME88K board's parallel printer port + * This driver attaches above the board-specific back-end. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/buf.h> +#include <sys/kernel.h> +#include <sys/ioctl.h> +#include <sys/uio.h> +#include <sys/device.h> +#include <sys/conf.h> +#include <sys/syslog.h> + +#include <machine/cpu.h> +#include <machine/bus.h> + +#include <dev/mvme/lptvar.h> + + +#define TIMEOUT hz*16 /* wait up to 16 seconds for a ready */ +#define STEP hz/4 + +#define LPTPRI (PZERO+8) +#define LPT_BSIZE 1024 + +#if !defined(DEBUG) || !defined(notdef) +#define LPRINTF(a) +#else +#define LPRINTF if (lptdebug) printf a +int lptdebug = 1; +#endif + +/* {b,c}devsw[] function prototypes */ +dev_type_open(lptopen); +dev_type_close(lptclose); +dev_type_write(lptwrite); +dev_type_ioctl(lptioctl); + + +#define LPTUNIT(s) (minor(s) & 0x0f) +#define LPTFLAGS(s) (minor(s) & 0xf0) + +static void lpt_wakeup __P((void *arg)); +static int pushbytes __P((struct lpt_softc *)); + +extern struct cfdriver lpt_cd; + + +void +lpt_attach_subr(sc) + struct lpt_softc *sc; +{ + + sc->sc_state = 0; + callout_init(&sc->sc_wakeup_ch); +} + +/* + * Reset the printer, then wait until it's selected and not busy. + */ +int +lptopen(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + int unit; + u_char flags; + struct lpt_softc *sc; + int error; + int spin; + + unit = LPTUNIT(dev); + flags = LPTFLAGS(dev); + + if (unit >= lpt_cd.cd_ndevs) + return (ENXIO); + sc = lpt_cd.cd_devs[unit]; + if (!sc) + return (ENXIO); + +#ifdef DIAGNOSTIC + if (sc->sc_state) + printf("%s: stat=0x%x not zero\n", sc->sc_dev.dv_xname, + sc->sc_state); +#endif + + if (sc->sc_state) + return (EBUSY); + + sc->sc_state = LPT_INIT; + sc->sc_flags = flags; + LPRINTF(("%s: open: flags=0x%x\n", sc->sc_dev.dv_xname, flags)); + + if ((flags & LPT_NOPRIME) == 0) { + /* assert Input Prime for 100 usec to start up printer */ + (sc->sc_funcs->lf_iprime) (sc); + } + + /* select fast or slow strobe depending on minor device number */ + if (flags & LPT_FAST_STROBE) + (sc->sc_funcs->lf_speed) (sc, LPT_STROBE_FAST); + else + (sc->sc_funcs->lf_speed) (sc, LPT_STROBE_SLOW); + + /* wait till ready (printer running diagnostics) */ + for (spin = 0; (sc->sc_funcs->lf_notrdy) (sc, 1); spin += STEP) { + if (spin >= TIMEOUT) { + sc->sc_state = 0; + return (EBUSY); + } + /* wait 1/4 second, give up if we get a signal */ + error = tsleep((caddr_t) sc, LPTPRI | PCATCH, "lptopen", STEP); + if (error != EWOULDBLOCK) { + sc->sc_state = 0; + return (error); + } + } + + sc->sc_inbuf = geteblk(LPT_BSIZE); + sc->sc_count = 0; + sc->sc_state = LPT_OPEN; + + if ((sc->sc_flags & LPT_NOINTR) == 0) + lpt_wakeup(sc); + + (sc->sc_funcs->lf_open) (sc, sc->sc_flags & LPT_NOINTR); + + LPRINTF(("%s: opened\n", sc->sc_dev.dv_xname)); + return (0); +} + +void +lpt_wakeup(arg) + void *arg; +{ + struct lpt_softc *sc; + int s; + + sc = arg; + + s = spltty(); + lpt_intr(sc); + splx(s); + + callout_reset(&sc->sc_wakeup_ch, STEP, lpt_wakeup, sc); +} + +/* + * Close the device, and free the local line buffer. + */ +int +lptclose(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + struct lpt_softc *sc; + int unit; + + unit = LPTUNIT(dev); + sc = lpt_cd.cd_devs[unit]; + + if (sc->sc_count) + (void) pushbytes(sc); + + if ((sc->sc_flags & LPT_NOINTR) == 0) + callout_stop(&sc->sc_wakeup_ch); + + (sc->sc_funcs->lf_close) (sc); + + sc->sc_state = 0; + brelse(sc->sc_inbuf); + + LPRINTF(("%s: closed\n", sc->sc_dev.dv_xname)); + return (0); +} + +int +pushbytes(sc) + struct lpt_softc *sc; +{ + int s, error, spin, tic; + + if (sc->sc_flags & LPT_NOINTR) { + while (sc->sc_count > 0) { + spin = 0; + while ((sc->sc_funcs->lf_notrdy) (sc, 0)) { + if (++spin < sc->sc_spinmax) + continue; + tic = 0; + /* adapt busy-wait algorithm */ + sc->sc_spinmax++; + while ((sc->sc_funcs->lf_notrdy) (sc, 1)) { + /* exponential backoff */ + tic = tic + tic + 1; + if (tic > TIMEOUT) + tic = TIMEOUT; + error = tsleep((caddr_t) sc, + LPTPRI | PCATCH, "lptpsh", tic); + if (error != EWOULDBLOCK) + return (error); + } + break; + } + + (sc->sc_funcs->lf_wrdata) (sc, *sc->sc_cp++); + sc->sc_count--; + + /* adapt busy-wait algorithm */ + if (spin * 2 + 16 < sc->sc_spinmax) + sc->sc_spinmax--; + } + } else { + while (sc->sc_count > 0) { + /* if the printer is ready for a char, give it one */ + if ((sc->sc_state & LPT_OBUSY) == 0) { + LPRINTF(("%s: write %d\n", sc->sc_dev.dv_xname, + sc->sc_count)); + s = spltty(); + (void) lpt_intr(sc); + splx(s); + } + error = tsleep((caddr_t) sc, LPTPRI | PCATCH, + "lptwrite2", 0); + if (error) + return (error); + } + } + return (0); +} + +/* + * Copy a line from user space to a local buffer, then call putc to get the + * chars moved to the output queue. + */ +int +lptwrite(dev, uio, flags) + dev_t dev; + struct uio *uio; + int flags; +{ + struct lpt_softc *sc; + size_t n; + int error; + + sc = lpt_cd.cd_devs[LPTUNIT(dev)]; + error = 0; + + while ((n = min(LPT_BSIZE, uio->uio_resid)) != 0) { + uiomove(sc->sc_cp = sc->sc_inbuf->b_data, n, uio); + sc->sc_count = n; + error = pushbytes(sc); + if (error) { + /* + * Return accurate residual if interrupted or timed + * out. + */ + uio->uio_resid += sc->sc_count; + sc->sc_count = 0; + return (error); + } + } + return (0); +} + +/* + * Handle printer interrupts which occur when the printer is ready to accept + * another char. + */ +int +lpt_intr(sc) + struct lpt_softc *sc; +{ + + if (sc->sc_count) { + /* send char */ + (sc->sc_funcs->lf_wrdata) (sc, *sc->sc_cp++); + sc->sc_count--; + sc->sc_state |= LPT_OBUSY; + } else + sc->sc_state &= ~LPT_OBUSY; + + if (sc->sc_count == 0) { + /* none, wake up the top half to get more */ + wakeup((caddr_t) sc); + } + + return (1); +} + +/* ARGSUSED */ +int +lptioctl(dev, cmd, data, flag, p) + dev_t dev; + u_long cmd; + caddr_t data; + int flag; + struct proc *p; +{ + + return (ENODEV); +} diff --git a/sys/dev/mvme/lpt_pcctwo.c b/sys/dev/mvme/lpt_pcctwo.c new file mode 100644 index 00000000000..9aa13c7e736 --- /dev/null +++ b/sys/dev/mvme/lpt_pcctwo.c @@ -0,0 +1,294 @@ +/* $NetBSD: lpt_pcctwo.c,v 1.1 2002/02/12 20:38:44 scw Exp $ */ + +/*- + * Copyright (c) 1999, 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Steve C. Woodford. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* + * Device Driver back-end for the PCCChip2's parallel printer port + */ + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/syslog.h> + +#include <machine/cpu.h> +#include <machine/bus.h> + +#include <dev/mvme/lptvar.h> +#include <dev/mvme/pcctworeg.h> +#include <dev/mvme/pcctwovar.h> + +/* + * Autoconfig stuff + */ +int lpt_pcctwo_match __P((struct device *, struct cfdata *, void *)); +void lpt_pcctwo_attach __P((struct device *, struct device *, void *)); + +struct cfattach lpt_pcctwo_ca = { + sizeof(struct lpt_softc), lpt_pcctwo_match, lpt_pcctwo_attach +}; + +extern struct cfdriver lpt_cd; + + +int lpt_pcctwo_intr __P((void *)); +void lpt_pcctwo_open __P((struct lpt_softc *, int)); +void lpt_pcctwo_close __P((struct lpt_softc *)); +void lpt_pcctwo_iprime __P((struct lpt_softc *)); +void lpt_pcctwo_speed __P((struct lpt_softc *, int)); +int lpt_pcctwo_notrdy __P((struct lpt_softc *, int)); +void lpt_pcctwo_wr_data __P((struct lpt_softc *, u_char)); + +struct lpt_funcs lpt_pcctwo_funcs = { + lpt_pcctwo_open, + lpt_pcctwo_close, + lpt_pcctwo_iprime, + lpt_pcctwo_speed, + lpt_pcctwo_notrdy, + lpt_pcctwo_wr_data +}; + +/* ARGSUSED */ +int +lpt_pcctwo_match(parent, cf, args) + struct device *parent; + struct cfdata *cf; + void *args; +{ + struct pcctwo_attach_args *pa; + + pa = args; + + if (strcmp(pa->pa_name, lpt_cd.cd_name)) + return (0); + +#ifdef MVME68K + if (machineid != MVME_167 && machineid != MVME_177) + return (0); +#endif + +#ifdef MVME88K + if (machineid != MVME_187) + return (0); +#endif + + pa->pa_ipl = cf->pcctwocf_ipl; + + return (1); +} + +/* ARGSUSED */ +void +lpt_pcctwo_attach(parent, self, args) + struct device *parent; + struct device *self; + void *args; +{ + struct pcctwo_attach_args *pa; + struct lpt_softc *sc; + + pa = (struct pcctwo_attach_args *) args; + sc = (struct lpt_softc *) self; + + /* The printer registers are part of the PCCChip2's own registers. */ + sc->sc_bust = pa->pa_bust; + bus_space_map(pa->pa_bust, pa->pa_offset, PCC2REG_SIZE, 0, + &sc->sc_bush); + + sc->sc_ipl = pa->pa_ipl & PCCTWO_ICR_LEVEL_MASK; + sc->sc_laststatus = 0; + sc->sc_funcs = &lpt_pcctwo_funcs; + + printf(": PCCchip2 Parallel Printer\n"); + + /* + * Disable interrupts until device is opened + */ + pcc2_reg_write(sc, PCC2REG_PRT_ACK_ICSR, 0); + pcc2_reg_write(sc, PCC2REG_PRT_FAULT_ICSR, 0); + pcc2_reg_write(sc, PCC2REG_PRT_SEL_ICSR, 0); + pcc2_reg_write(sc, PCC2REG_PRT_PE_ICSR, 0); + pcc2_reg_write(sc, PCC2REG_PRT_BUSY_ICSR, 0); + pcc2_reg_write(sc, PCC2REG_PRT_CONTROL, 0); + + /* + * Main attachment code + */ + lpt_attach_subr(sc); + + /* Register the event counter */ + evcnt_attach_dynamic(&sc->sc_evcnt, EVCNT_TYPE_INTR, + pcctwointr_evcnt(sc->sc_ipl), "printer", sc->sc_dev.dv_xname); + + /* + * Hook into the printer interrupt + */ + pcctwointr_establish(PCCTWOV_PRT_ACK, lpt_pcctwo_intr, sc->sc_ipl, sc, + &sc->sc_evcnt); +} + +/* + * Handle printer interrupts + */ +int +lpt_pcctwo_intr(arg) + void *arg; +{ + struct lpt_softc *sc; + int i; + + sc = (struct lpt_softc *) arg; + + /* is printer online and ready for output */ + if (lpt_pcctwo_notrdy(sc, 0) || lpt_pcctwo_notrdy(sc, 1)) + return (0); + + i = lpt_intr(sc); + + if (pcc2_reg_read(sc, PCC2REG_PRT_INPUT_STATUS) & PCCTWO_PRT_IN_SR_PINT) + pcc2_reg_write(sc, PCC2REG_PRT_ACK_ICSR, + sc->sc_icr | PCCTWO_ICR_ICLR); + + return (i); +} + +void +lpt_pcctwo_open(sc, int_ena) + struct lpt_softc *sc; + int int_ena; +{ + int sps; + + pcc2_reg_write(sc, PCC2REG_PRT_ACK_ICSR, + PCCTWO_ICR_ICLR | PCCTWO_ICR_EDGE); + + pcc2_reg_write(sc, PCC2REG_PRT_CONTROL, + pcc2_reg_read(sc, PCC2REG_PRT_CONTROL) | PCCTWO_PRT_CTRL_DOEN); + + if (int_ena == 0) { + sps = splhigh(); + sc->sc_icr = sc->sc_ipl | PCCTWO_ICR_EDGE; + pcc2_reg_write(sc, PCC2REG_PRT_ACK_ICSR, sc->sc_icr); + splx(sps); + } +} + +void +lpt_pcctwo_close(sc) + struct lpt_softc *sc; +{ + + pcc2_reg_write(sc, PCC2REG_PRT_ACK_ICSR, + PCCTWO_ICR_ICLR | PCCTWO_ICR_EDGE); + pcc2_reg_write(sc, PCC2REG_PRT_CONTROL, 0); +} + +void +lpt_pcctwo_iprime(sc) + struct lpt_softc *sc; +{ + + pcc2_reg_write(sc, PCC2REG_PRT_CONTROL, + pcc2_reg_read(sc, PCC2REG_PRT_CONTROL) | PCCTWO_PRT_CTRL_INP); + + delay(100); + + pcc2_reg_write(sc, PCC2REG_PRT_CONTROL, + pcc2_reg_read(sc, PCC2REG_PRT_CONTROL) & ~PCCTWO_PRT_CTRL_INP); + + delay(100); +} + +void +lpt_pcctwo_speed(sc, speed) + struct lpt_softc *sc; + int speed; +{ + u_int8_t reg; + + reg = pcc2_reg_read(sc, PCC2REG_PRT_CONTROL); + + if (speed == LPT_STROBE_FAST) + reg |= PCCTWO_PRT_CTRL_FAST; + else + reg &= ~PCCTWO_PRT_CTRL_FAST; + + pcc2_reg_write(sc, PCC2REG_PRT_CONTROL, reg); +} + +int +lpt_pcctwo_notrdy(sc, err) + struct lpt_softc *sc; + int err; +{ + u_int8_t status; + u_int8_t new; + +#define LPS_INVERT (PCCTWO_PRT_IN_SR_SEL) +#define LPS_MASK (PCCTWO_PRT_IN_SR_SEL | PCCTWO_PRT_IN_SR_FLT | \ + PCCTWO_PRT_IN_SR_BSY | PCCTWO_PRT_IN_SR_PE) + + status = pcc2_reg_read(sc, PCC2REG_PRT_INPUT_STATUS) ^ LPS_INVERT; + status &= LPS_MASK; + + if (err) { + new = status & ~sc->sc_laststatus; + sc->sc_laststatus = status; + + if (new & PCCTWO_PRT_IN_SR_SEL) + log(LOG_NOTICE, "%s: offline\n", + sc->sc_dev.dv_xname); + else if (new & PCCTWO_PRT_IN_SR_PE) + log(LOG_NOTICE, "%s: out of paper\n", + sc->sc_dev.dv_xname); + else if (new & PCCTWO_PRT_IN_SR_FLT) + log(LOG_NOTICE, "%s: output error\n", + sc->sc_dev.dv_xname); + } + + return (status); +} + +void +lpt_pcctwo_wr_data(sc, data) + struct lpt_softc *sc; + u_char data; +{ + + pcc2_reg_write16(sc, PCC2REG_PRT_DATA, (u_int16_t) data); +} diff --git a/sys/dev/mvme/lptvar.h b/sys/dev/mvme/lptvar.h new file mode 100644 index 00000000000..5b461396545 --- /dev/null +++ b/sys/dev/mvme/lptvar.h @@ -0,0 +1,95 @@ +/* $NetBSD: lptvar.h,v 1.1 2002/02/12 20:38:45 scw Exp $ */ + +/*- + * Copyright (c) 1999, 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Steve C. Woodford. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* + * Common front-end for mvme68k/mvme88k parallel printer ports + */ + +#ifndef __mvme_lptvar_h +#define __mvme_lptvar_h + +#include <sys/callout.h> + +struct lpt_funcs; + + +struct lpt_softc { + struct device sc_dev; + struct callout sc_wakeup_ch; + struct lpt_funcs *sc_funcs; + void *sc_arg; + size_t sc_count; + struct buf *sc_inbuf; + u_char *sc_cp; + int sc_spinmax; + u_char sc_state; +#define LPT_OPEN 0x01 /* device is open */ +#define LPT_OBUSY 0x02 /* printer is busy doing output */ +#define LPT_INIT 0x04 /* waiting to initialize for open */ + u_char sc_flags; +#define LPT_FAST_STROBE 0x10 /* Select 1.6uS strobe pulse */ +#define LPT_AUTOLF 0x20 /* automatic LF on CR */ +#define LPT_NOPRIME 0x40 /* don't prime on open */ +#define LPT_NOINTR 0x80 /* do not use interrupt */ + + /* Back-end specific stuff */ + bus_space_tag_t sc_bust; + bus_space_handle_t sc_bush; + int sc_ipl; + u_char sc_icr; + u_char sc_laststatus; + struct evcnt sc_evcnt; +}; + + +struct lpt_funcs { + void (*lf_open) __P((struct lpt_softc *, int)); + void (*lf_close) __P((struct lpt_softc *)); + void (*lf_iprime) __P((struct lpt_softc *)); + void (*lf_speed) __P((struct lpt_softc *, int)); + int (*lf_notrdy) __P((struct lpt_softc *, int)); + void (*lf_wrdata) __P((struct lpt_softc *, u_char)); +}; + +#define LPT_STROBE_FAST 0 +#define LPT_STROBE_SLOW 1 + +extern void lpt_attach_subr __P((struct lpt_softc *)); +extern int lpt_intr __P((struct lpt_softc *)); + +#endif /* __mvme_lptvar_h */ diff --git a/sys/dev/mvme/memc.c b/sys/dev/mvme/memc.c new file mode 100644 index 00000000000..49c9825b081 --- /dev/null +++ b/sys/dev/mvme/memc.c @@ -0,0 +1,640 @@ +/* $NetBSD: memc.c,v 1.1 2002/02/12 20:38:46 scw Exp $ */ + +/*- + * Copyright (c) 2000, 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Steve C. Woodford. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* + * Support for the MEMECC and MEMC40 memory controllers on MVME68K + * and MVME88K boards. + */ + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/malloc.h> + +#include <machine/cpu.h> +#include <machine/bus.h> + +#include <dev/mvme/memcvar.h> +#include <dev/mvme/memcreg.h> +#include <dev/mvme/pcctwovar.h> +#include <dev/mvme/pcctworeg.h> + +#include <dev/vme/vmevar.h> +#include <dev/mvme/mvmebus.h> +#include <dev/mvme/vme_twovar.h> +#include <dev/mvme/vme_tworeg.h> + + +static struct memc_softc *memc_softcs[MEMC_NDEVS]; +static int memc_softc_count; + +static void memc040_attach(struct memc_softc *); +static void memecc_attach(struct memc_softc *); +static void memc_hook_error_intr(struct memc_softc *, int (*)(void *)); + +static int memecc_err_intr(void *); +static void memecc_log_error(struct memc_softc *, u_int8_t, int, int); + +#define MEMECC_SCRUBBER_PERIOD 86400 /* ~24 hours */ + +/* + * The following stuff is used to decode the ECC syndrome code so + * that we can figure out exactly which address/bit needed to be + * corrected. + */ +#define MEMECC_SYN_BIT_MASK 0x0fu +#define MEMECC_SYN_BANK_A (0x00u << 4) +#define MEMECC_SYN_BANK_B (0x01u << 4) +#define MEMECC_SYN_BANK_C (0x02u << 4) +#define MEMECC_SYN_BANK_D (0x03u << 4) +#define MEMECC_SYN_BANK_SHIFT 4 +#define MEMECC_SYN_BANK_MASK 0x03u +#define MEMECC_SYN_CHECKBIT_ERR 0x80u +#define MEMECC_SYN_INVALID 0xffu + +static u_int8_t memc_syn_decode[256] = { + MEMECC_SYN_INVALID, /* 0x00 */ + MEMECC_SYN_CHECKBIT_ERR | 0, /* 0x01: Checkbit 0 */ + MEMECC_SYN_CHECKBIT_ERR | 1, /* 0x02: Checkbit 1 */ + MEMECC_SYN_INVALID, /* 0x03 */ + MEMECC_SYN_CHECKBIT_ERR | 2, /* 0x04: Checkbit 2 */ + MEMECC_SYN_INVALID, /* 0x05 */ + MEMECC_SYN_INVALID, /* 0x06 */ + MEMECC_SYN_BANK_C | 10, /* 0x07: Bank C 10/26 */ + MEMECC_SYN_CHECKBIT_ERR | 3, /* 0x08: Checkbit 3 */ + MEMECC_SYN_INVALID, /* 0x09 */ + MEMECC_SYN_INVALID, /* 0x0a */ + MEMECC_SYN_BANK_C | 13, /* 0x0b: Bank C 13/29 */ + MEMECC_SYN_INVALID, /* 0x0c */ + MEMECC_SYN_BANK_D | 1, /* 0x0d: Bank D 1/17 */ + MEMECC_SYN_BANK_D | 2, /* 0x0e: Bank D 2/18 */ + MEMECC_SYN_INVALID, /* 0x0f */ + MEMECC_SYN_CHECKBIT_ERR | 4, /* 0x10: Checkbit 4 */ + MEMECC_SYN_INVALID, /* 0x11 */ + MEMECC_SYN_INVALID, /* 0x12 */ + MEMECC_SYN_BANK_C | 14, /* 0x13: Bank C 14/30 */ + MEMECC_SYN_INVALID, /* 0x14 */ + MEMECC_SYN_BANK_D | 4, /* 0x15: Bank D 4/20 */ + MEMECC_SYN_BANK_D | 5, /* 0x16: Bank D 5/21 */ + MEMECC_SYN_INVALID, /* 0x17 */ + MEMECC_SYN_INVALID, /* 0x18 */ + MEMECC_SYN_BANK_D | 8, /* 0x19: Bank D 8/24 */ + MEMECC_SYN_BANK_D | 9, /* 0x1a: Bank D 9/25 */ + MEMECC_SYN_INVALID, /* 0x1b */ + MEMECC_SYN_BANK_D | 10, /* 0x1c: Bank D 10/26 */ + MEMECC_SYN_INVALID, /* 0x1d */ + MEMECC_SYN_INVALID, /* 0x1e */ + MEMECC_SYN_INVALID, /* 0x1f */ + MEMECC_SYN_CHECKBIT_ERR | 5, /* 0x20: Checkbit 5 */ + MEMECC_SYN_INVALID, /* 0x21 */ + MEMECC_SYN_INVALID, /* 0x22 */ + MEMECC_SYN_BANK_C | 0, /* 0x23: Bank C 0/16 */ + MEMECC_SYN_INVALID, /* 0x24 */ + MEMECC_SYN_BANK_D | 7, /* 0x25: Bank D 7/23 */ + MEMECC_SYN_BANK_D | 6, /* 0x26: Bank D 6/22 */ + MEMECC_SYN_INVALID, /* 0x27 */ + MEMECC_SYN_INVALID, /* 0x28 */ + MEMECC_SYN_BANK_A | 15, /* 0x29: Bank A 15/31 */ + MEMECC_SYN_BANK_D | 12, /* 0x2a: Bank D 12/28 */ + MEMECC_SYN_INVALID, /* 0x2b */ + MEMECC_SYN_BANK_D | 13, /* 0x2c: Bank D 13/29 */ + MEMECC_SYN_INVALID, /* 0x2d */ + MEMECC_SYN_INVALID, /* 0x2e */ + MEMECC_SYN_INVALID, /* 0x2f */ + MEMECC_SYN_INVALID, /* 0x30 */ + MEMECC_SYN_BANK_A | 14, /* 0x31: Bank A 14/30 */ + MEMECC_SYN_BANK_A | 0, /* 0x32: Bank A 0/16 */ + MEMECC_SYN_INVALID, /* 0x33 */ + MEMECC_SYN_BANK_A | 1, /* 0x34: Bank A 1/17 */ + MEMECC_SYN_INVALID, /* 0x35 */ + MEMECC_SYN_INVALID, /* 0x36 */ + MEMECC_SYN_INVALID, /* 0x37 */ + MEMECC_SYN_BANK_A | 2, /* 0x38: Bank A 2/18 */ + MEMECC_SYN_INVALID, /* 0x39 */ + MEMECC_SYN_INVALID, /* 0x3a */ + MEMECC_SYN_INVALID, /* 0x3b */ + MEMECC_SYN_INVALID, /* 0x3c */ + MEMECC_SYN_BANK_C | 3, /* 0x3d: Bank C 3/19 */ + MEMECC_SYN_INVALID, /* 0x3e */ + MEMECC_SYN_INVALID, /* 0x3f */ + MEMECC_SYN_CHECKBIT_ERR | 6, /* 0x40: Checkbit 6 */ + MEMECC_SYN_INVALID, /* 0x41 */ + MEMECC_SYN_INVALID, /* 0x42 */ + MEMECC_SYN_BANK_C | 1, /* 0x43: Bank C 1/17 */ + MEMECC_SYN_INVALID, /* 0x44 */ + MEMECC_SYN_BANK_C | 4, /* 0x45: Bank C 4/20 */ + MEMECC_SYN_BANK_C | 8, /* 0x46: Bank C 8/24 */ + MEMECC_SYN_INVALID, /* 0x47 */ + MEMECC_SYN_INVALID, /* 0x48 */ + MEMECC_SYN_BANK_C | 7, /* 0x49: Bank C 7/23 */ + MEMECC_SYN_BANK_D | 15, /* 0x4a: Bank D 15/31 */ + MEMECC_SYN_INVALID, /* 0x4b */ + MEMECC_SYN_BANK_D | 14, /* 0x4c: Bank D 14/30 */ + MEMECC_SYN_INVALID, /* 0x4d */ + MEMECC_SYN_INVALID, /* 0x4e */ + MEMECC_SYN_BANK_B | 3, /* 0x4f: Bank B 3/19 */ + MEMECC_SYN_INVALID, /* 0x50 */ + MEMECC_SYN_BANK_B | 4, /* 0x51: Bank B 4/20 */ + MEMECC_SYN_BANK_B | 7, /* 0x52: Bank B 7/23 */ + MEMECC_SYN_INVALID, /* 0x53 */ + MEMECC_SYN_BANK_A | 4, /* 0x54: Bank A 4/20 */ + MEMECC_SYN_INVALID, /* 0x55 */ + MEMECC_SYN_INVALID, /* 0x56 */ + MEMECC_SYN_INVALID, /* 0x57 */ + MEMECC_SYN_BANK_A | 5, /* 0x58: Bank A 5/21 */ + MEMECC_SYN_INVALID, /* 0x59 */ + MEMECC_SYN_INVALID, /* 0x5a */ + MEMECC_SYN_INVALID, /* 0x5b */ + MEMECC_SYN_INVALID, /* 0x5c */ + MEMECC_SYN_INVALID, /* 0x5d */ + MEMECC_SYN_INVALID, /* 0x5e */ + MEMECC_SYN_INVALID, /* 0x5f */ + MEMECC_SYN_INVALID, /* 0x60 */ + MEMECC_SYN_BANK_B | 5, /* 0x61: Bank B 5/21 */ + MEMECC_SYN_BANK_B | 6, /* 0x62: Bank B 6/22 */ + MEMECC_SYN_INVALID, /* 0x63 */ + MEMECC_SYN_BANK_A | 8, /* 0x64: Bank A 8/24 */ + MEMECC_SYN_INVALID, /* 0x65 */ + MEMECC_SYN_INVALID, /* 0x66 */ + MEMECC_SYN_INVALID, /* 0x67 */ + MEMECC_SYN_BANK_A | 9, /* 0x68: Bank A 9/25 */ + MEMECC_SYN_INVALID, /* 0x69 */ + MEMECC_SYN_INVALID, /* 0x6a */ + MEMECC_SYN_INVALID, /* 0x6b */ + MEMECC_SYN_INVALID, /* 0x6c */ + MEMECC_SYN_INVALID, /* 0x6d */ + MEMECC_SYN_INVALID, /* 0x6e */ + MEMECC_SYN_INVALID, /* 0x6f */ + MEMECC_SYN_BANK_A | 10, /* 0x70: Bank A 10/26 */ + MEMECC_SYN_INVALID, /* 0x71 */ + MEMECC_SYN_INVALID, /* 0x72 */ + MEMECC_SYN_INVALID, /* 0x73 */ + MEMECC_SYN_INVALID, /* 0x74 */ + MEMECC_SYN_INVALID, /* 0x75 */ + MEMECC_SYN_INVALID, /* 0x76 */ + MEMECC_SYN_INVALID, /* 0x77 */ + MEMECC_SYN_INVALID, /* 0x78 */ + MEMECC_SYN_INVALID, /* 0x79 */ + MEMECC_SYN_BANK_C | 11, /* 0x7a: Bank C 11/27 */ + MEMECC_SYN_INVALID, /* 0x7b */ + MEMECC_SYN_INVALID, /* 0x7c */ + MEMECC_SYN_INVALID, /* 0x7d */ + MEMECC_SYN_INVALID, /* 0x7e */ + MEMECC_SYN_INVALID, /* 0x7f */ + MEMECC_SYN_CHECKBIT_ERR | 7, /* 0x80: Checkbit 7 */ + MEMECC_SYN_INVALID, /* 0x81 */ + MEMECC_SYN_INVALID, /* 0x82 */ + MEMECC_SYN_BANK_C | 2, /* 0x83: Bank C 2/18 */ + MEMECC_SYN_INVALID, /* 0x84 */ + MEMECC_SYN_BANK_C | 5, /* 0x85: Bank C 5/21 */ + MEMECC_SYN_BANK_C | 9, /* 0x86: Bank C 9/25 */ + MEMECC_SYN_INVALID, /* 0x87 */ + MEMECC_SYN_INVALID, /* 0x88 */ + MEMECC_SYN_BANK_C | 6, /* 0x89: Bank C 6/22 */ + MEMECC_SYN_BANK_C | 12, /* 0x8a: Bank C 12/28 */ + MEMECC_SYN_INVALID, /* 0x8b */ + MEMECC_SYN_BANK_D | 0, /* 0x8c: Bank D 0/16 */ + MEMECC_SYN_INVALID, /* 0x8d */ + MEMECC_SYN_INVALID, /* 0x8e */ + MEMECC_SYN_INVALID, /* 0x8f */ + MEMECC_SYN_INVALID, /* 0x90 */ + MEMECC_SYN_BANK_B | 8, /* 0x91: Bank B 8/24 */ + MEMECC_SYN_BANK_C | 15, /* 0x92: Bank C 15/31 */ + MEMECC_SYN_INVALID, /* 0x93 */ + MEMECC_SYN_BANK_A | 7, /* 0x94: Bank A 7/23 */ + MEMECC_SYN_INVALID, /* 0x95 */ + MEMECC_SYN_INVALID, /* 0x96 */ + MEMECC_SYN_INVALID, /* 0x97 */ + MEMECC_SYN_BANK_A | 6, /* 0x98: Bank A 6/22 */ + MEMECC_SYN_INVALID, /* 0x99 */ + MEMECC_SYN_INVALID, /* 0x9a */ + MEMECC_SYN_INVALID, /* 0x9b */ + MEMECC_SYN_INVALID, /* 0x9c */ + MEMECC_SYN_INVALID, /* 0x9d */ + MEMECC_SYN_BANK_B | 11, /* 0x9e: Bank B 11/27 */ + MEMECC_SYN_INVALID, /* 0x9f */ + MEMECC_SYN_INVALID, /* 0xa0 */ + MEMECC_SYN_BANK_B | 9, /* 0xa1: Bank B 9/25 */ + MEMECC_SYN_BANK_B | 12, /* 0xa2: Bank B 12/28 */ + MEMECC_SYN_INVALID, /* 0xa3 */ + MEMECC_SYN_BANK_B | 15, /* 0xa4: Bank B 15/31 */ + MEMECC_SYN_INVALID, /* 0xa5 */ + MEMECC_SYN_INVALID, /* 0xa6 */ + MEMECC_SYN_BANK_A | 11, /* 0xa7: Bank A 11/27 */ + MEMECC_SYN_BANK_A | 12, /* 0xa8: Bank A 12/28 */ + MEMECC_SYN_INVALID, /* 0xa9 */ + MEMECC_SYN_INVALID, /* 0xaa */ + MEMECC_SYN_INVALID, /* 0xab */ + MEMECC_SYN_INVALID, /* 0xac */ + MEMECC_SYN_INVALID, /* 0xad */ + MEMECC_SYN_INVALID, /* 0xae */ + MEMECC_SYN_INVALID, /* 0xaf */ + MEMECC_SYN_BANK_A | 13, /* 0xb0: Bank A 13/29 */ + MEMECC_SYN_INVALID, /* 0xb1 */ + MEMECC_SYN_INVALID, /* 0xb2 */ + MEMECC_SYN_INVALID, /* 0xb3 */ + MEMECC_SYN_INVALID, /* 0xb4 */ + MEMECC_SYN_INVALID, /* 0xb5 */ + MEMECC_SYN_INVALID, /* 0xb6 */ + MEMECC_SYN_INVALID, /* 0xb7 */ + MEMECC_SYN_INVALID, /* 0xb8 */ + MEMECC_SYN_INVALID, /* 0xb9 */ + MEMECC_SYN_INVALID, /* 0xba */ + MEMECC_SYN_INVALID, /* 0xbb */ + MEMECC_SYN_INVALID, /* 0xbc */ + MEMECC_SYN_INVALID, /* 0xbd */ + MEMECC_SYN_INVALID, /* 0xbe */ + MEMECC_SYN_INVALID, /* 0xbf */ + MEMECC_SYN_INVALID, /* 0xc0 */ + MEMECC_SYN_BANK_B | 10, /* 0xc1: Bank B 10/26 */ + MEMECC_SYN_BANK_B | 13, /* 0xc2: Bank B 13/29 */ + MEMECC_SYN_INVALID, /* 0xc3 */ + MEMECC_SYN_BANK_B | 14, /* 0xc4: Bank B 14/30 */ + MEMECC_SYN_INVALID, /* 0xc5 */ + MEMECC_SYN_INVALID, /* 0xc6 */ + MEMECC_SYN_INVALID, /* 0xc7 */ + MEMECC_SYN_BANK_B | 0, /* 0xc8: Bank B 0/16 */ + MEMECC_SYN_INVALID, /* 0xc9 */ + MEMECC_SYN_INVALID, /* 0xca */ + MEMECC_SYN_INVALID, /* 0xcb */ + MEMECC_SYN_INVALID, /* 0xcc */ + MEMECC_SYN_INVALID, /* 0xcd */ + MEMECC_SYN_INVALID, /* 0xce */ + MEMECC_SYN_INVALID, /* 0xcf */ + MEMECC_SYN_BANK_B | 1, /* 0xd0: Bank B 1/17 */ + MEMECC_SYN_INVALID, /* 0xd1 */ + MEMECC_SYN_INVALID, /* 0xd2 */ + MEMECC_SYN_BANK_A | 3, /* 0xd3: Bank A 3/19 */ + MEMECC_SYN_INVALID, /* 0xd4 */ + MEMECC_SYN_INVALID, /* 0xd5 */ + MEMECC_SYN_INVALID, /* 0xd6 */ + MEMECC_SYN_INVALID, /* 0xd7 */ + MEMECC_SYN_INVALID, /* 0xd8 */ + MEMECC_SYN_INVALID, /* 0xd9 */ + MEMECC_SYN_INVALID, /* 0xda */ + MEMECC_SYN_INVALID, /* 0xdb */ + MEMECC_SYN_INVALID, /* 0xdc */ + MEMECC_SYN_INVALID, /* 0xdd */ + MEMECC_SYN_INVALID, /* 0xde */ + MEMECC_SYN_INVALID, /* 0xdf */ + MEMECC_SYN_BANK_B | 2, /* 0xe0: Bank B 2/18 */ + MEMECC_SYN_INVALID, /* 0xe1 */ + MEMECC_SYN_INVALID, /* 0xe2 */ + MEMECC_SYN_INVALID, /* 0xe3 */ + MEMECC_SYN_INVALID, /* 0xe4 */ + MEMECC_SYN_INVALID, /* 0xe5 */ + MEMECC_SYN_INVALID, /* 0xe6 */ + MEMECC_SYN_INVALID, /* 0xe7 */ + MEMECC_SYN_INVALID, /* 0xe8 */ + MEMECC_SYN_BANK_D | 11, /* 0xe9: Bank D 11/27 */ + MEMECC_SYN_INVALID, /* 0xea */ + MEMECC_SYN_INVALID, /* 0xeb */ + MEMECC_SYN_INVALID, /* 0xec */ + MEMECC_SYN_INVALID, /* 0xed */ + MEMECC_SYN_INVALID, /* 0xee */ + MEMECC_SYN_INVALID, /* 0xef */ + MEMECC_SYN_INVALID, /* 0xf0 */ + MEMECC_SYN_INVALID, /* 0xf1 */ + MEMECC_SYN_INVALID, /* 0xf2 */ + MEMECC_SYN_INVALID, /* 0xf3 */ + MEMECC_SYN_BANK_D | 3, /* 0xf4: Bank D 3/19 */ + MEMECC_SYN_INVALID, /* 0xf5 */ + MEMECC_SYN_INVALID, /* 0xf6 */ + MEMECC_SYN_INVALID, /* 0xf7 */ + MEMECC_SYN_INVALID, /* 0xf8 */ + MEMECC_SYN_INVALID, /* 0xf9 */ + MEMECC_SYN_INVALID, /* 0xfa */ + MEMECC_SYN_INVALID, /* 0xfb */ + MEMECC_SYN_INVALID, /* 0xfc */ + MEMECC_SYN_INVALID, /* 0xfd */ + MEMECC_SYN_INVALID, /* 0xfe */ + MEMECC_SYN_INVALID /* 0xff */ +}; + + +/* ARGSUSED */ +void +memc_init(sc) + struct memc_softc *sc; +{ + u_int8_t chipid; + u_int8_t memcfg; + + if (memc_softc_count == MEMC_NDEVS) + panic("memc_attach: too many memc devices!"); + + memc_softcs[memc_softc_count++] = sc; + + chipid = memc_reg_read(sc, MEMC_REG_CHIP_ID); + memcfg = memc_reg_read(sc, MEMC_REG_MEMORY_CONFIG); + + printf(": %dMB %s Memory Controller Chip (Rev %d)\n", + MEMC_MEMORY_CONFIG_2_MB(memcfg), + (chipid == MEMC_CHIP_ID_MEMC040) ? "Parity" : "ECC", + memc_reg_read(sc, MEMC_REG_CHIP_REVISION)); + + printf("%s: Base Address: 0x%x, ", sc->sc_dev.dv_xname, + MEMC_BASE_ADDRESS(memc_reg_read(sc, MEMC_REG_BASE_ADDRESS_HI), + memc_reg_read(sc, MEMC_REG_BASE_ADDRESS_LO))); + + printf("Fast RAM Read %sabled\n", (memc_reg_read(sc, + MEMC_REG_MEMORY_CONFIG) & MEMC_MEMORY_CONFIG_FSTRD) ? + "En" : "Dis"); + + switch (chipid) { + case MEMC_CHIP_ID_MEMC040: + memc040_attach(sc); + break; + case MEMC_CHIP_ID_MEMECC: + memecc_attach(sc); + break; + } +} + +static void +memc040_attach(struct memc_softc *sc) +{ + + /* XXX: TBD */ +} + +static void +memecc_attach(struct memc_softc *sc) +{ + u_int8_t rv; + + /* + * First, disable bus-error and interrupts on ECC errors. + * Also switch off SWAIT to enhance performance. + */ + rv = memc_reg_read(sc, MEMECC_REG_DRAM_CONTROL); + rv &= ~(MEMECC_DRAM_CONTROL_NCEBEN | + MEMECC_DRAM_CONTROL_NCEIEN | + MEMECC_DRAM_CONTROL_SWAIT); + rv |= MEMECC_DRAM_CONTROL_RAMEN; + memc_reg_write(sc, MEMECC_REG_DRAM_CONTROL, rv); + rv = memc_reg_read(sc, MEMECC_REG_SCRUB_CONTROL); + rv &= ~(MEMECC_SCRUB_CONTROL_SCRBEN | MEMECC_SCRUB_CONTROL_SBEIEN); + memc_reg_write(sc, MEMECC_REG_SCRUB_CONTROL, rv); + + /* + * Ensure error correction is enabled + */ + rv = memc_reg_read(sc, MEMECC_REG_DATA_CONTROL); + rv &= ~MEMECC_DATA_CONTROL_DERC; + memc_reg_write(sc, MEMECC_REG_DATA_CONTROL, rv); + + /* + * Clear any error currently in the logs + */ + rv = memc_reg_read(sc, MEMECC_REG_ERROR_LOGGER); +#ifdef DIAGNOSTIC + if ((rv & MEMECC_ERROR_LOGGER_MASK) != 0) + memecc_log_error(sc, rv, 0, 0); +#endif + memc_reg_write(sc, MEMECC_REG_ERROR_LOGGER, + MEMECC_ERROR_LOGGER_ERRLOG); + + rv = memc_reg_read(sc, MEMECC_REG_ERROR_LOGGER + 2); +#ifdef DIAGNOSTIC + if ((rv & MEMECC_ERROR_LOGGER_MASK) != 0) + memecc_log_error(sc, rv, 2, 0); +#endif + memc_reg_write(sc, MEMECC_REG_ERROR_LOGGER + 2, + MEMECC_ERROR_LOGGER_ERRLOG); + + /* + * Now hook the ECC error interrupt + */ + if (memc_softc_count == 1) + memc_hook_error_intr(sc, memecc_err_intr); + + /* + * Enable bus-error and interrupt on uncorrectable ECC + */ + rv = memc_reg_read(sc, MEMECC_REG_DRAM_CONTROL); + rv |= MEMECC_DRAM_CONTROL_NCEBEN | MEMECC_DRAM_CONTROL_NCEIEN; + memc_reg_write(sc, MEMECC_REG_DRAM_CONTROL, rv); + + /* + * Set up the scrubber to run roughly once every 24 hours + * with minimal impact on the local bus. With these on/off + * time settings, a scrub of a 32MB DRAM board will take + * roughly half a minute. + */ + memc_reg_write(sc, MEMECC_REG_SCRUB_PERIOD_HI, + MEMECC_SCRUB_PERIOD_HI(MEMECC_SCRUBBER_PERIOD)); + memc_reg_write(sc, MEMECC_REG_SCRUB_PERIOD_LO, + MEMECC_SCRUB_PERIOD_LO(MEMECC_SCRUBBER_PERIOD)); + memc_reg_write(sc, MEMECC_REG_SCRUB_TIME_ONOFF, + MEMECC_SCRUB_TIME_ON_1 | MEMECC_SCRUB_TIME_OFF_16); + + /* + * Start the scrubber, and enable interrupts on Correctable errors + */ + memc_reg_write(sc, MEMECC_REG_SCRUB_CONTROL, + memc_reg_read(sc, MEMECC_REG_SCRUB_CONTROL) | + MEMECC_SCRUB_CONTROL_SCRBEN | MEMECC_SCRUB_CONTROL_SBEIEN); + + printf("%s: Logging ECC errors at ipl %d\n", sc->sc_dev.dv_xname, + MEMC_IRQ_LEVEL); +} + +static void +memc_hook_error_intr(struct memc_softc *sc, int (*func)(void *)) +{ + +#if 0 + evcnt_attach_dynamic(&sc->sc_evcnt, EVCNT_TYPE_INTR, + (*sc->sc_isrevcnt)(sc->sc_isrcookie, MEMC_IRQ_LEVEL), + "memory", "ecc errors"); +#endif + + /* + * On boards without a VMEChip2, the interrupt is routed + * via the MCChip (mvme162/mvme172). + */ + if (vmetwo_not_present) + pcctwointr_establish(MCCHIPV_PARITY_ERR, func, MEMC_IRQ_LEVEL, + sc, &sc->sc_evcnt); + else + vmetwo_local_intr_establish(MEMC_IRQ_LEVEL, + VME2_VEC_PARITY_ERROR, func, sc, &sc->sc_evcnt); +} + +/* ARGSUSED */ +static int +memecc_err_intr(void *arg) +{ + struct memc_softc *sc; + u_int8_t rv; + int i, j, cnt = 0; + + /* + * For each memory controller we found ... + */ + for (i = 0; i < memc_softc_count; i++) { + sc = memc_softcs[i]; + + /* + * There are two error loggers per controller, the registers of + * the 2nd are offset from the 1st by 2 bytes. + */ + for (j = 0; j <= 2; j += 2) { + rv = memc_reg_read(sc, MEMECC_REG_ERROR_LOGGER + j); + if ((rv & MEMECC_ERROR_LOGGER_MASK) != 0) { + memecc_log_error(sc, rv, j, 1); + memc_reg_write(sc, MEMECC_REG_ERROR_LOGGER + j, + MEMECC_ERROR_LOGGER_ERRLOG); + cnt++; + } + } + } + + return (cnt); +} + +/* + * Log an ECC error to the console. + * Note: Since this usually runs at an elevated ipl (above clock), we + * should probably schedule a soft interrupt to log the error details. + * (But only for errors where we would not normally panic.) + */ +static void +memecc_log_error(struct memc_softc *sc, u_int8_t errlog, int off, int mbepanic) +{ + u_int32_t addr; + u_int8_t rv, syndrome; + const char *bm = "CPU"; + const char *rdwr; + const char *etype; + char syntext[32]; + + /* + * Get the address associated with the error. + */ + rv = memc_reg_read(sc, MEMECC_REG_ERROR_ADDRESS_HIHI + off); + addr = (u_int32_t)rv; + rv = memc_reg_read(sc, MEMECC_REG_ERROR_ADDRESS_HI + off); + addr = (addr << 8) | (u_int32_t)rv; + rv = memc_reg_read(sc, MEMECC_REG_ERROR_ADDRESS_MID + off); + addr = (addr << 8) | (u_int32_t)rv; + rv = memc_reg_read(sc, MEMECC_REG_ERROR_ADDRESS_LO + off); + addr = (addr << 8) | (u_int32_t)rv; + + /* + * And the Syndrome bits + */ + syndrome = memc_reg_read(sc, MEMECC_REG_ERROR_SYNDROME + off); + + rdwr = ((errlog & MEMECC_ERROR_LOGGER_ERD) != 0) ? " read" : " write"; + + if ((errlog & MEMECC_ERROR_LOGGER_EALT) != 0) + bm = "Peripheral Device"; + else + if ((errlog & MEMECC_ERROR_LOGGER_ESCRB) != 0) { + bm = "Scrubber"; + rdwr = ""; + } + + if ((errlog & MEMECC_ERROR_LOGGER_SBE) != 0) { + int syncode, bank, bitnum; + + etype = "Correctable"; + syncode = memc_syn_decode[syndrome]; + bitnum = (syncode & MEMECC_SYN_BIT_MASK) + (off ? 16 : 0); + bank = (syncode >> MEMECC_SYN_BANK_SHIFT) &MEMECC_SYN_BANK_MASK; + + if (syncode == MEMECC_SYN_INVALID) + strcpy(syntext, "Invalid!"); + else + if ((syncode & MEMECC_SYN_CHECKBIT_ERR) != 0) + sprintf(syntext, "Checkbit#%d", bitnum); + else { + addr |= (u_int32_t) (bank << 2); + sprintf(syntext, "DRAM Bank %c, Bit#%d", + 'A' + bank, bitnum); + } + } else if ((errlog & MEMECC_ERROR_LOGGER_MBE) != 0) + etype = "Uncorrectable"; + else + etype = "Spurious"; + + printf("%s: %s error on %s%s access to 0x%08x.\n", + sc->sc_dev.dv_xname, etype, bm, rdwr, addr); + + if ((errlog & MEMECC_ERROR_LOGGER_SBE) != 0) + printf("%s: ECC Syndrome 0x%02x (%s)\n", sc->sc_dev.dv_xname, + syndrome, syntext); + + /* + * If an uncorrectable error was detected by an alternate + * bus master or the scrubber, panic immediately. + * We can't rely on the contents of memory at this point. + * + * Uncorrectable errors detected when the CPU was accessing + * DRAM will cause the CPU to take a bus error trap. Depending + * on whether the error was in kernel or user mode, the system + * with either panic or kill the affected process. Basically, + * we don't have to deal with it here. + * + * XXX: I'm not sure whether it's our responsibility to + * perform some dummy writes to the offending address in this + * case to re-generate a good ECC. Note that we'd have to write + * an entire block of 4 words since we can only narrow down the + * faulty address for correctable errors... + */ + if (mbepanic && (errlog & MEMECC_ERROR_LOGGER_MBE) && + (errlog & (MEMECC_ERROR_LOGGER_ESCRB|MEMECC_ERROR_LOGGER_EALT))) { + /* + * Ensure we don't get a Bus Error while panicing... + */ + rv = memc_reg_read(sc, MEMECC_REG_DRAM_CONTROL + off); + rv &= ~(MEMECC_DRAM_CONTROL_NCEBEN | + MEMECC_DRAM_CONTROL_NCEIEN); + memc_reg_write(sc, MEMECC_REG_DRAM_CONTROL + off, rv); + rv = memc_reg_read(sc, MEMECC_REG_SCRUB_CONTROL + off); + rv &= ~(MEMECC_SCRUB_CONTROL_SBEIEN | + MEMECC_SCRUB_CONTROL_SCRBEN); + memc_reg_write(sc, MEMECC_REG_SCRUB_CONTROL + off, rv); + + panic("%s: Halting system to preserve data integrity.", + sc->sc_dev.dv_xname); + } +} diff --git a/sys/dev/mvme/memcreg.h b/sys/dev/mvme/memcreg.h new file mode 100644 index 00000000000..e75bb013f91 --- /dev/null +++ b/sys/dev/mvme/memcreg.h @@ -0,0 +1,177 @@ +/* $NetBSD: memcreg.h,v 1.1 2002/02/12 20:38:46 scw Exp $ */ + +/*- + * Copyright (c) 2000, 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Steve C. Woodford. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* + * Register definitions for the MEMECC and MEMC040 devices. + */ +#ifndef _MVME_MEMCREG_H +#define _MVME_MEMCREG_H + +/* + * Size, in bytes, of the memory controller's register set + * (Actually, the MEMC040's register set is only 0x20 bytes in size, but + * we go with the larger of the two). + */ +#define MEMC_REGSIZE 0x80 + +/* Both memory controllers share some registers in common */ +#define MEMC_REG_CHIP_ID 0x00 +#define MEMC_CHIP_ID_MEMC040 0x80 /* It's a MEMC040 */ +#define MEMC_CHIP_ID_MEMECC 0x81 /* It's a MEMECC */ + +/* Revision of the ASIC */ +#define MEMC_REG_CHIP_REVISION 0x04 + +/* Configuration of the memory block controlled by this ASIC */ +#define MEMC_REG_MEMORY_CONFIG 0x08 +#define MEMC_MEMORY_CONFIG_2_BYTES(x) (0x400000 << ((x) & 0x07)) +#define MEMC_MEMORY_CONFIG_2_MB(x) (4 << ((x) & 0x07)) +#define MEMC040_MEMORY_CONFIG_EXTPEN (1u << 3) /* External parity enabled */ +#define MEMC040_MEMORY_CONFIG_WPB (1u << 4) /* Write Per Bit mode */ +#define MEMC_MEMORY_CONFIG_FSTRD (1u << 5) /* Fast RAM Read enabled */ + +/* Where, in the cpu's address space, does this memory appear? */ +#define MEMC_REG_BASE_ADDRESS_HI 0x14 +#define MEMC_REG_BASE_ADDRESS_LO 0x18 +#define MEMC_BASE_ADDRESS(hi,lo) (((hi) << 24) | (((lo) & 0xc0) << 22)) + +/* Tells the memory controller what the board's Bus Clock frequency is */ +#define MEMC_REG_BUS_CLOCK 0x1c + + +/* Register offsets and definitions for the Parity Memory Controller */ +#define MEMC040_REG_ALT_STATUS 0x0c /* Not used */ +#define MEMC040_REG_ALT_CONTROL 0x10 /* Not used */ + +/* Memory Control Register */ +#define MEMC040_REG_RAM_CONTROL 0x18 +#define MEMC040_RAM_CONTROL_RAMEN (1u << 0) +#define MEMC040_RAM_CONTROL_PAREN (1u << 1) +#define MEMC040_RAM_CONTROL_PARINT (1u << 2) +#define MEMC040_RAM_CONTROL_WWP (1u << 3) +#define MEMC040_RAM_CONTROL_SWAIT (1u << 4) +#define MEMC040_RAM_CONTROL_DMCTL (1u << 5) + + +/* Register offsets and definitions for the ECC Memory Controller */ +#define MEMECC_REG_DRAM_CONTROL 0x18 +#define MEMECC_DRAM_CONTROL_RAMEN (1u << 0) +#define MEMECC_DRAM_CONTROL_NCEBEN (1u << 1) +#define MEMECC_DRAM_CONTROL_NCEIEN (1u << 2) +#define MEMECC_DRAM_CONTROL_RWB3 (1u << 3) +#define MEMECC_DRAM_CONTROL_SWAIT (1u << 4) +#define MEMECC_DRAM_CONTROL_RWB5 (1u << 5) +#define MEMECC_DRAM_CONTROL_BAD22 (1u << 6) +#define MEMECC_DRAM_CONTROL_BAD23 (1u << 7) + +#define MEMECC_REG_DATA_CONTROL 0x20 +#define MEMECC_DATA_CONTROL_RWCKB (1u << 3) +#define MEMECC_DATA_CONTROL_ZFILL (1u << 4) +#define MEMECC_DATA_CONTROL_DERC (1u << 5) + +#define MEMECC_REG_SCRUB_CONTROL 0x24 +#define MEMECC_SCRUB_CONTROL_IDIS (1u << 0) +#define MEMECC_SCRUB_CONTROL_SBEIEN (1u << 1) +#define MEMECC_SCRUB_CONTROL_SCRBEN (1u << 3) +#define MEMECC_SCRUB_CONTROL_SCRB (1u << 4) +#define MEMECC_SCRUB_CONTROL_HITDIS (1u << 5) +#define MEMECC_SCRUB_CONTROL_RADATA (1u << 6) +#define MEMECC_SCRUB_CONTROL_RACODE (1u << 7) + +#define MEMECC_REG_SCRUB_PERIOD_HI 0x28 +#define MEMECC_SCRUB_PERIOD_HI(secs) (((secs) / 2) >> 8) +#define MEMECC_REG_SCRUB_PERIOD_LO 0x2c +#define MEMECC_SCRUB_PERIOD_LO(secs) (((secs) / 2) & 0xffu) + +#define MEMECC_REG_CHIP_PRESCALE 0x30 + +#define MEMECC_REG_SCRUB_TIME_ONOFF 0x34 +#define MEMECC_SCRUB_TIME_ONOFF_MASK 0x07u +#define MEMECC_SCRUB_TIME_OFF_0 0u +#define MEMECC_SCRUB_TIME_OFF_16 1u +#define MEMECC_SCRUB_TIME_OFF_32 2u +#define MEMECC_SCRUB_TIME_OFF_64 3u +#define MEMECC_SCRUB_TIME_OFF_128 4u +#define MEMECC_SCRUB_TIME_OFF_256 5u +#define MEMECC_SCRUB_TIME_OFF_512 6u +#define MEMECC_SCRUB_TIME_OFF_NEVER 7u +#define MEMECC_SCRUB_TIME_ON_1 (0u << 3) +#define MEMECC_SCRUB_TIME_ON_16 (1u << 3) +#define MEMECC_SCRUB_TIME_ON_32 (2u << 3) +#define MEMECC_SCRUB_TIME_ON_64 (3u << 3) +#define MEMECC_SCRUB_TIME_ON_128 (4u << 3) +#define MEMECC_SCRUB_TIME_ON_256 (5u << 3) +#define MEMECC_SCRUB_TIME_ON_512 (6u << 3) +#define MEMECC_SCRUB_TIME_ON_ALWAYS (7u << 3) +#define MEMECC_SCRUB_TIME_SRDIS (1u << 7) + +#define MEMECC_REG_SCRUB_PRESCALE_HI 0x38 +#define MEMECC_REG_SCRUB_PRESCALE_MID 0x3c +#define MEMECC_REG_SCRUB_PRESCALE_LO 0x40 + +#define MEMECC_REG_SCRUB_TIMER_HI 0x44 +#define MEMECC_REG_SCRUB_TIMER_LO 0x48 + +#define MEMECC_REG_SCRUB_ADDR_CNTR_HIHI 0x4c +#define MEMECC_REG_SCRUB_ADDR_CNTR_HI 0x50 +#define MEMECC_REG_SCRUB_ADDR_CNTR_MID 0x54 +#define MEMECC_REG_SCRUB_ADDR_CNTR_LO 0x58 + +#define MEMECC_REG_ERROR_LOGGER 0x5c +#define MEMECC_ERROR_LOGGER_MASK 0x7fu +#define MEMECC_ERROR_LOGGER_SBE (1u << 0) +#define MEMECC_ERROR_LOGGER_MBE (1u << 1) +#define MEMECC_ERROR_LOGGER_EALT (1u << 3) +#define MEMECC_ERROR_LOGGER_ERA (1u << 4) +#define MEMECC_ERROR_LOGGER_ESCRB (1u << 5) +#define MEMECC_ERROR_LOGGER_ERD (1u << 6) +#define MEMECC_ERROR_LOGGER_ERRLOG (1u << 7) + +#define MEMECC_REG_ERROR_ADDRESS_HIHI 0x60 +#define MEMECC_REG_ERROR_ADDRESS_HI 0x64 +#define MEMECC_REG_ERROR_ADDRESS_MID 0x68 +#define MEMECC_REG_ERROR_ADDRESS_LO 0x6c + +#define MEMECC_REG_ERROR_SYNDROME 0x70 + +#define MEMECC_REG_DEFAULTS1 0x74 +#define MEMECC_REG_DEFAULTS2 0x78 + +#define MEMECC_REG_SDRAM_CONFIG 0x7c + +#endif /* _MVME_MEMCREG_H */ diff --git a/sys/dev/mvme/memcvar.h b/sys/dev/mvme/memcvar.h new file mode 100644 index 00000000000..c38f596d016 --- /dev/null +++ b/sys/dev/mvme/memcvar.h @@ -0,0 +1,69 @@ +/* $NetBSD: memcvar.h,v 1.1 2002/02/12 20:38:46 scw Exp $ */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Steve C. Woodford. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#ifndef _MVME_MEMCVAR_H +#define _MVME_MEMCVAR_H + +struct memc_softc { + struct device sc_dev; + bus_space_tag_t sc_bust; + bus_space_handle_t sc_bush; + struct evcnt sc_evcnt; +}; + +#define MEMC_NDEVS 2 + +#define memc_reg_read(sc, off) \ + bus_space_read_1((sc)->sc_bust, (sc)->sc_bush, (off)) +#define memc_reg_write(sc, off, v) \ + bus_space_write_1((sc)->sc_bust, (sc)->sc_bush, (off), (v)) + +/* + * Some tweakable parameters. Mind you, I don't recommend changing + * the ipl... + * XXX: This should probably be ipl 7 + */ +#ifdef MVME68K +#define MEMC_IRQ_LEVEL 6 +#else +#error Define irq level for memory contoller +#endif + +extern void memc_init(struct memc_softc *); + +#endif /* _MVME_MEMCREG_H */ diff --git a/sys/dev/mvme/mvmebus.c b/sys/dev/mvme/mvmebus.c new file mode 100644 index 00000000000..c71f54bf389 --- /dev/null +++ b/sys/dev/mvme/mvmebus.c @@ -0,0 +1,961 @@ +/* $NetBSD: mvmebus.c,v 1.1 2002/02/12 20:38:47 scw Exp $ */ + +/*- + * Copyright (c) 2000, 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Steve C. Woodford. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/malloc.h> +#include <sys/kcore.h> + +#include <machine/cpu.h> +#include <machine/bus.h> + +#include <dev/vme/vmereg.h> +#include <dev/vme/vmevar.h> + +#include <dev/mvme/mvmebus.h> + +#ifdef DIAGNOSTIC +int mvmebus_dummy_dmamap_create(bus_dma_tag_t, bus_size_t, int, bus_size_t, + bus_size_t, int, bus_dmamap_t *); +void mvmebus_dummy_dmamap_destroy(bus_dma_tag_t, bus_dmamap_t); +int mvmebus_dummy_dmamem_alloc(bus_dma_tag_t, bus_size_t, bus_size_t, + bus_size_t, bus_dma_segment_t *, int, int *, int); +void mvmebus_dummy_dmamem_free(bus_dma_tag_t, bus_dma_segment_t *, int); +#endif + +#ifdef DEBUG +static const char *mvmebus_mod_string(vme_addr_t, vme_size_t, + vme_am_t, vme_datasize_t); +#endif + +static void mvmebus_offboard_ram(struct mvmebus_softc *); +static int mvmebus_dmamap_load_common(struct mvmebus_softc *, bus_dmamap_t); + +vme_am_t _mvmebus_am_cap[] = { + MVMEBUS_AM_CAP_BLKD64 | MVMEBUS_AM_CAP_USER, + MVMEBUS_AM_CAP_DATA | MVMEBUS_AM_CAP_USER, + MVMEBUS_AM_CAP_PROG | MVMEBUS_AM_CAP_USER, + MVMEBUS_AM_CAP_BLK | MVMEBUS_AM_CAP_USER, + MVMEBUS_AM_CAP_BLKD64 | MVMEBUS_AM_CAP_SUPER, + MVMEBUS_AM_CAP_DATA | MVMEBUS_AM_CAP_SUPER, + MVMEBUS_AM_CAP_PROG | MVMEBUS_AM_CAP_SUPER, + MVMEBUS_AM_CAP_BLK | MVMEBUS_AM_CAP_SUPER +}; + +const char *mvmebus_irq_name[] = { + "vmeirq0", "vmeirq1", "vmeirq2", "vmeirq3", + "vmeirq4", "vmeirq5", "vmeirq6", "vmeirq7" +}; + +extern phys_ram_seg_t mem_clusters[0]; +extern int mem_cluster_cnt; + + +static void +mvmebus_offboard_ram(sc) + struct mvmebus_softc *sc; +{ + struct mvmebus_range *svr, *mvr; + vme_addr_t start, end, size; + int i; + + /* + * If we have any offboard RAM (i.e. a VMEbus RAM board) then + * we need to record its details since it's effectively another + * VMEbus slave image as far as we're concerned. + * The chip-specific backend will have reserved sc->sc_slaves[0] + * for exactly this purpose. + */ + svr = sc->sc_slaves; + if (mem_cluster_cnt < 2) { + svr->vr_am = MVMEBUS_AM_DISABLED; + return; + } + + start = mem_clusters[1].start; + size = mem_clusters[1].size - 1; + end = start + size; + + /* + * Figure out which VMEbus master image the RAM is + * visible through. This will tell us the address + * modifier and datasizes it uses, as well as allowing + * us to calculate its `real' VMEbus address. + * + * XXX FIXME: This is broken if the RAM is mapped through + * a translated address space. For example, on mvme167 it's + * perfectly legal to set up the following A32 mapping: + * + * vr_locaddr == 0x80000000 + * vr_vmestart == 0x10000000 + * vr_vmeend == 0x10ffffff + * + * In this case, RAM at VMEbus address 0x10800000 will appear at local + * address 0x80800000, but we need to set the slave vr_vmestart to + * 0x10800000. + */ + for (i = 0, mvr = sc->sc_masters; i < sc->sc_nmasters; i++, mvr++) { + vme_addr_t vstart = mvr->vr_locstart + mvr->vr_vmestart; + + if (start >= vstart && + end <= vstart + (mvr->vr_vmeend - mvr->vr_vmestart)) + break; + } + if (i == sc->sc_nmasters) { + svr->vr_am = MVMEBUS_AM_DISABLED; +#ifdef DEBUG + printf("%s: No VMEbus master mapping for offboard RAM!\n", + sc->sc_dev.dv_xname); +#endif + return; + } + + svr->vr_locstart = start; + svr->vr_vmestart = start & mvr->vr_mask; + svr->vr_vmeend = svr->vr_vmestart + size; + svr->vr_datasize = mvr->vr_datasize; + svr->vr_mask = mvr->vr_mask; + svr->vr_am = mvr->vr_am & VME_AM_ADRSIZEMASK; + svr->vr_am |= MVMEBUS_AM_CAP_DATA | MVMEBUS_AM_CAP_PROG | + MVMEBUS_AM_CAP_SUPER | MVMEBUS_AM_CAP_USER; +} + +void +mvmebus_attach(sc) + struct mvmebus_softc *sc; +{ + struct vmebus_attach_args vaa; + int i; + + /* Zap the IRQ reference counts */ + for (i = 0; i < 8; i++) + sc->sc_irqref[i] = 0; + + /* If there's offboard RAM, get its VMEbus slave attributes */ + mvmebus_offboard_ram(sc); + +#ifdef DEBUG + for (i = 0; i < sc->sc_nmasters; i++) { + struct mvmebus_range *vr = &sc->sc_masters[i]; + if (vr->vr_am == MVMEBUS_AM_DISABLED) { + printf("%s: Master#%d: disabled\n", + sc->sc_dev.dv_xname, i); + continue; + } + printf("%s: Master#%d: 0x%08lx -> %s\n", + sc->sc_dev.dv_xname, i, + vr->vr_locstart + (vr->vr_vmestart & vr->vr_mask), + mvmebus_mod_string(vr->vr_vmestart, + (vr->vr_vmeend - vr->vr_vmestart) + 1, + vr->vr_am, vr->vr_datasize)); + } + + for (i = 0; i < sc->sc_nslaves; i++) { + struct mvmebus_range *vr = &sc->sc_slaves[i]; + if (vr->vr_am == MVMEBUS_AM_DISABLED) { + printf("%s: Slave#%d: disabled\n", + sc->sc_dev.dv_xname, i); + continue; + } + printf("%s: Slave#%d: 0x%08lx -> %s\n", + sc->sc_dev.dv_xname, i, vr->vr_locstart, + mvmebus_mod_string(vr->vr_vmestart, + (vr->vr_vmeend - vr->vr_vmestart) + 1, + vr->vr_am, vr->vr_datasize)); + } +#endif + + sc->sc_vct.cookie = sc; + sc->sc_vct.vct_probe = mvmebus_probe; + sc->sc_vct.vct_map = mvmebus_map; + sc->sc_vct.vct_unmap = mvmebus_unmap; + sc->sc_vct.vct_int_map = mvmebus_intmap; + sc->sc_vct.vct_int_evcnt = mvmebus_intr_evcnt; + sc->sc_vct.vct_int_establish = mvmebus_intr_establish; + sc->sc_vct.vct_int_disestablish = mvmebus_intr_disestablish; + sc->sc_vct.vct_dmamap_create = mvmebus_dmamap_create; + sc->sc_vct.vct_dmamap_destroy = mvmebus_dmamap_destroy; + sc->sc_vct.vct_dmamem_alloc = mvmebus_dmamem_alloc; + sc->sc_vct.vct_dmamem_free = mvmebus_dmamem_free; + + sc->sc_mvmedmat._cookie = sc; + sc->sc_mvmedmat._dmamap_load = mvmebus_dmamap_load; + sc->sc_mvmedmat._dmamap_load_mbuf = mvmebus_dmamap_load_mbuf; + sc->sc_mvmedmat._dmamap_load_uio = mvmebus_dmamap_load_uio; + sc->sc_mvmedmat._dmamap_load_raw = mvmebus_dmamap_load_raw; + sc->sc_mvmedmat._dmamap_unload = mvmebus_dmamap_unload; + sc->sc_mvmedmat._dmamap_sync = mvmebus_dmamap_sync; + sc->sc_mvmedmat._dmamem_map = mvmebus_dmamem_map; + sc->sc_mvmedmat._dmamem_unmap = mvmebus_dmamem_unmap; + sc->sc_mvmedmat._dmamem_mmap = mvmebus_dmamem_mmap; + +#ifdef DIAGNOSTIC + sc->sc_mvmedmat._dmamap_create = mvmebus_dummy_dmamap_create; + sc->sc_mvmedmat._dmamap_destroy = mvmebus_dummy_dmamap_destroy; + sc->sc_mvmedmat._dmamem_alloc = mvmebus_dummy_dmamem_alloc; + sc->sc_mvmedmat._dmamem_free = mvmebus_dummy_dmamem_free; +#else + sc->sc_mvmedmat._dmamap_create = NULL; + sc->sc_mvmedmat._dmamap_destroy = NULL; + sc->sc_mvmedmat._dmamem_alloc = NULL; + sc->sc_mvmedmat._dmamem_free = NULL; +#endif + + vaa.va_vct = &sc->sc_vct; + vaa.va_bdt = &sc->sc_mvmedmat; + vaa.va_slaveconfig = NULL; + + config_found(&sc->sc_dev, &vaa, 0); +} + +int +mvmebus_map(vsc, vmeaddr, len, am, datasize, swap, tag, handle, resc) + void *vsc; + vme_addr_t vmeaddr; + vme_size_t len; + vme_am_t am; + vme_datasize_t datasize; + vme_swap_t swap; + bus_space_tag_t *tag; + bus_space_handle_t *handle; + vme_mapresc_t *resc; +{ + struct mvmebus_softc *sc; + struct mvmebus_mapresc *mr; + struct mvmebus_range *vr; + vme_addr_t end; + vme_am_t cap, as; + paddr_t paddr; + int rv, i; + + sc = vsc; + end = (vmeaddr + len) - 1; + paddr = 0; + vr = sc->sc_masters; + cap = MVMEBUS_AM2CAP(am); + as = am & VME_AM_ADRSIZEMASK; + + for (i = 0; i < sc->sc_nmasters && paddr == 0; i++, vr++) { + if (vr->vr_am == MVMEBUS_AM_DISABLED) + continue; + + if (cap == (vr->vr_am & cap) && + as == (vr->vr_am & VME_AM_ADRSIZEMASK) && + datasize <= vr->vr_datasize && + vmeaddr >= vr->vr_vmestart && end < vr->vr_vmeend) + paddr = vr->vr_locstart + (vmeaddr & vr->vr_mask); + } + if (paddr == 0) + return (ENOMEM); + + rv = bus_space_map(sc->sc_bust, paddr, len, 0, handle); + if (rv != 0) + return (rv); + + /* Allocate space for the resource tag */ + if ((mr = malloc(sizeof(*mr), M_DEVBUF, M_NOWAIT)) == NULL) { + bus_space_unmap(sc->sc_bust, *handle, len); + return (ENOMEM); + } + + /* Record the range's details */ + mr->mr_am = am; + mr->mr_datasize = datasize; + mr->mr_addr = vmeaddr; + mr->mr_size = len; + mr->mr_handle = *handle; + mr->mr_range = i; + + *tag = sc->sc_bust; + *resc = (vme_mapresc_t *) mr; + + return (0); +} + +/* ARGSUSED */ +void +mvmebus_unmap(vsc, resc) + void *vsc; + vme_mapresc_t resc; +{ + struct mvmebus_softc *sc = vsc; + struct mvmebus_mapresc *mr = (struct mvmebus_mapresc *) resc; + + bus_space_unmap(sc->sc_bust, mr->mr_handle, mr->mr_size); + + free(mr, M_DEVBUF); +} + +int +mvmebus_probe(vsc, vmeaddr, len, am, datasize, callback, arg) + void *vsc; + vme_addr_t vmeaddr; + vme_size_t len; + vme_am_t am; + vme_datasize_t datasize; + int (*callback)(void *, bus_space_tag_t, bus_space_handle_t); + void *arg; +{ + bus_space_tag_t tag; + bus_space_handle_t handle; + vme_mapresc_t resc; + vme_size_t offs; + int rv; + + /* Get a temporary mapping to the VMEbus range */ + rv = mvmebus_map(vsc, vmeaddr, len, am, datasize, 0, + &tag, &handle, &resc); + if (rv) + return (rv); + + if (callback) + rv = (*callback) (arg, tag, handle); + else + for (offs = 0; offs < len && rv == 0;) { + switch (datasize) { + case VME_D8: + rv = bus_space_peek_1(tag, handle, offs, NULL); + offs += 1; + break; + + case VME_D16: + rv = bus_space_peek_2(tag, handle, offs, NULL); + offs += 2; + break; + + case VME_D32: + rv = bus_space_peek_4(tag, handle, offs, NULL); + offs += 4; + break; + } + } + + mvmebus_unmap(vsc, resc); + + return (rv); +} + +/* ARGSUSED */ +int +mvmebus_intmap(vsc, level, vector, handlep) + void *vsc; + int level, vector; + vme_intr_handle_t *handlep; +{ + + if (level < 1 || level > 7 || vector < 0x80 || vector > 0xff) + return (EINVAL); + + /* This is rather gross */ + *handlep = (void *) (int) ((level << 8) | vector); + return (0); +} + +/* ARGSUSED */ +const struct evcnt * +mvmebus_intr_evcnt(vsc, handle) + void *vsc; + vme_intr_handle_t handle; +{ + struct mvmebus_softc *sc = vsc; + + return (&sc->sc_evcnt[(((int) handle) >> 8) - 1]); +} + +void * +mvmebus_intr_establish(vsc, handle, prior, func, arg) + void *vsc; + vme_intr_handle_t handle; + int prior; + int (*func)(void *); + void *arg; +{ + struct mvmebus_softc *sc; + int level, vector, first; + + sc = vsc; + + /* Extract the interrupt's level and vector */ + level = ((int) handle) >> 8; + vector = ((int) handle) & 0xff; + +#ifdef DIAGNOSTIC + if (vector < 0 || vector > 0xff) { + printf("%s: Illegal vector offset: 0x%x\n", + sc->sc_dev.dv_xname, vector); + panic("mvmebus_intr_establish"); + } + if (level < 1 || level > 7) { + printf("%s: Illegal interrupt level: %d\n", + sc->sc_dev.dv_xname, level); + panic("mvmebus_intr_establish"); + } +#endif + + first = (sc->sc_irqref[level]++ == 0); + + (*sc->sc_intr_establish)(sc->sc_chip, prior, level, vector, first, + func, arg, &sc->sc_evcnt[level - 1]); + + return ((void *) handle); +} + +void +mvmebus_intr_disestablish(vsc, handle) + void *vsc; + vme_intr_handle_t handle; +{ + struct mvmebus_softc *sc; + int level, vector, last; + + sc = vsc; + + /* Extract the interrupt's level and vector */ + level = ((int) handle) >> 8; + vector = ((int) handle) & 0xff; + +#ifdef DIAGNOSTIC + if (vector < 0 || vector > 0xff) { + printf("%s: Illegal vector offset: 0x%x\n", + sc->sc_dev.dv_xname, vector); + panic("mvmebus_intr_disestablish"); + } + if (level < 1 || level > 7) { + printf("%s: Illegal interrupt level: %d\n", + sc->sc_dev.dv_xname, level); + panic("mvmebus_intr_disestablish"); + } + if (sc->sc_irqref[level] == 0) { + printf("%s: VMEirq#%d: Reference count already zero!\n", + sc->sc_dev.dv_xname, level); + panic("mvmebus_intr_disestablish"); + } +#endif + + last = (--(sc->sc_irqref[level]) == 0); + + (*sc->sc_intr_disestablish)(sc->sc_chip, level, vector, last, + &sc->sc_evcnt[level - 1]); +} + +#ifdef DIAGNOSTIC +/* ARGSUSED */ +int +mvmebus_dummy_dmamap_create(t, size, nsegs, maxsegsz, boundary, flags, dmamp) + bus_dma_tag_t t; + bus_size_t size; + int nsegs; + bus_size_t maxsegsz; + bus_size_t boundary; + int flags; + bus_dmamap_t *dmamp; +{ + + panic("Must use vme_dmamap_create() in place of bus_dmamap_create()"); + return (0); /* Shutup the compiler */ +} + +/* ARGSUSED */ +void +mvmebus_dummy_dmamap_destroy(t, map) + bus_dma_tag_t t; + bus_dmamap_t map; +{ + + panic("Must use vme_dmamap_destroy() in place of bus_dmamap_destroy()"); +} +#endif + +/* ARGSUSED */ +int +mvmebus_dmamap_create(vsc, len, am, datasize, swap, nsegs, + segsz, bound, flags, mapp) + void *vsc; + vme_size_t len; + vme_am_t am; + vme_datasize_t datasize; + vme_swap_t swap; + int nsegs; + vme_size_t segsz; + vme_addr_t bound; + int flags; + bus_dmamap_t *mapp; +{ + struct mvmebus_softc *sc = vsc; + struct mvmebus_dmamap *vmap; + struct mvmebus_range *vr; + vme_am_t cap, as; + int i, rv; + + cap = MVMEBUS_AM2CAP(am); + as = am & VME_AM_ADRSIZEMASK; + + /* + * Verify that we even stand a chance of satisfying + * the VMEbus address space and datasize requested. + */ + for (i = 0, vr = sc->sc_slaves; i < sc->sc_nslaves; i++, vr++) { + if (vr->vr_am == MVMEBUS_AM_DISABLED) + continue; + + if (as == (vr->vr_am & VME_AM_ADRSIZEMASK) && + cap == (vr->vr_am & cap) && datasize <= vr->vr_datasize && + len <= (vr->vr_vmeend - vr->vr_vmestart)) + break; + } + + if (i == sc->sc_nslaves) + return (EINVAL); + + if ((vmap = malloc(sizeof(*vmap), M_DMAMAP, + (flags & BUS_DMA_NOWAIT) ? M_NOWAIT : M_WAITOK)) == NULL) + return (ENOMEM); + + + rv = bus_dmamap_create(sc->sc_dmat, len, nsegs, segsz, + bound, flags, mapp); + if (rv != 0) { + free(vmap, M_DMAMAP); + return (rv); + } + + vmap->vm_am = am; + vmap->vm_datasize = datasize; + vmap->vm_swap = swap; + vmap->vm_slave = vr; + + (*mapp)->_dm_cookie = vmap; + + return (0); +} + +void +mvmebus_dmamap_destroy(vsc, map) + void *vsc; + bus_dmamap_t map; +{ + struct mvmebus_softc *sc = vsc; + + free(map->_dm_cookie, M_DMAMAP); + bus_dmamap_destroy(sc->sc_dmat, map); +} + +static int +mvmebus_dmamap_load_common(sc, map) + struct mvmebus_softc *sc; + bus_dmamap_t map; +{ + struct mvmebus_dmamap *vmap = map->_dm_cookie; + struct mvmebus_range *vr = vmap->vm_slave; + bus_dma_segment_t *ds; + vme_am_t cap, am; + int i; + + cap = MVMEBUS_AM2CAP(vmap->vm_am); + am = vmap->vm_am & VME_AM_ADRSIZEMASK; + + /* + * Traverse the list of segments which make up this map, and + * convert the cpu-relative addresses therein to VMEbus addresses. + */ + for (ds = &map->dm_segs[0]; ds < &map->dm_segs[map->dm_nsegs]; ds++) { + /* + * First, see if this map's slave image can access the + * segment, otherwise we have to waste time scanning all + * the slave images. + */ + vr = vmap->vm_slave; + if (am == (vr->vr_am & VME_AM_ADRSIZEMASK) && + cap == (vr->vr_am & cap) && + vmap->vm_datasize <= vr->vr_datasize && + ds->_ds_cpuaddr >= vr->vr_locstart && + ds->ds_len <= (vr->vr_vmeend - vr->vr_vmestart)) + goto found; + + for (i = 0, vr = sc->sc_slaves; i < sc->sc_nslaves; i++, vr++) { + if (vr->vr_am == MVMEBUS_AM_DISABLED) + continue; + + /* + * Filter out any slave images which don't have the + * same VMEbus address modifier and datasize as + * this DMA map, and those which don't cover the + * physical address region containing the segment. + */ + if (vr != vmap->vm_slave && + am == (vr->vr_am & VME_AM_ADRSIZEMASK) && + cap == (vr->vr_am & cap) && + vmap->vm_datasize <= vr->vr_datasize && + ds->_ds_cpuaddr >= vr->vr_locstart && + ds->ds_len <= (vr->vr_vmeend - vr->vr_vmestart)) + break; + } + + /* + * Did we find an applicable slave image which covers this + * segment? + */ + if (i == sc->sc_nslaves) { + /* + * XXX TODO: + * + * Bounce this segment via a bounce buffer allocated + * from this DMA map. + */ + printf("mvmebus_dmamap_load_common: bounce needed!\n"); + return (EINVAL); + } + +found: + /* + * Generate the VMEbus address of this segment + */ + ds->ds_addr = (ds->_ds_cpuaddr - vr->vr_locstart) + + vr->vr_vmestart; + } + + return (0); +} + +int +mvmebus_dmamap_load(t, map, buf, buflen, p, flags) + bus_dma_tag_t t; + bus_dmamap_t map; + void *buf; + bus_size_t buflen; + struct proc *p; + int flags; +{ + struct mvmebus_softc *sc = t->_cookie; + int rv; + + rv = bus_dmamap_load(sc->sc_dmat, map, buf, buflen, p, flags); + if (rv != 0) + return rv; + + return mvmebus_dmamap_load_common(sc, map); +} + +int +mvmebus_dmamap_load_mbuf(t, map, chain, flags) + bus_dma_tag_t t; + bus_dmamap_t map; + struct mbuf *chain; + int flags; +{ + struct mvmebus_softc *sc = t->_cookie; + int rv; + + rv = bus_dmamap_load_mbuf(sc->sc_dmat, map, chain, flags); + if (rv != 0) + return rv; + + return mvmebus_dmamap_load_common(sc, map); +} + +int +mvmebus_dmamap_load_uio(t, map, uio, flags) + bus_dma_tag_t t; + bus_dmamap_t map; + struct uio *uio; + int flags; +{ + struct mvmebus_softc *sc = t->_cookie; + int rv; + + rv = bus_dmamap_load_uio(sc->sc_dmat, map, uio, flags); + if (rv != 0) + return rv; + + return mvmebus_dmamap_load_common(sc, map); +} + +int +mvmebus_dmamap_load_raw(t, map, segs, nsegs, size, flags) + bus_dma_tag_t t; + bus_dmamap_t map; + bus_dma_segment_t *segs; + int nsegs; + bus_size_t size; + int flags; +{ + struct mvmebus_softc *sc = t->_cookie; + int rv; + + /* + * mvmebus_dmamem_alloc() will ensure that the physical memory + * backing these segments is 100% accessible in at least one + * of the board's VMEbus slave images. + */ + rv = bus_dmamap_load_raw(sc->sc_dmat, map, segs, nsegs, size, flags); + if (rv != 0) + return rv; + + return mvmebus_dmamap_load_common(sc, map); +} + +void +mvmebus_dmamap_unload(t, map) + bus_dma_tag_t t; + bus_dmamap_t map; +{ + struct mvmebus_softc *sc = t->_cookie; + + /* XXX Deal with bounce buffers */ + + bus_dmamap_unload(sc->sc_dmat, map); +} + +void +mvmebus_dmamap_sync(t, map, offset, len, ops) + bus_dma_tag_t t; + bus_dmamap_t map; + bus_addr_t offset; + bus_size_t len; + int ops; +{ + struct mvmebus_softc *sc = t->_cookie; + + /* XXX Bounce buffers */ + + bus_dmamap_sync(sc->sc_dmat, map, offset, len, ops); +} + +#ifdef DIAGNOSTIC +/* ARGSUSED */ +int +mvmebus_dummy_dmamem_alloc(t, size, align, boundary, segs, nsegs, rsegs, flags) + bus_dma_tag_t t; + bus_size_t size; + bus_size_t align; + bus_size_t boundary; + bus_dma_segment_t *segs; + int nsegs; + int *rsegs; + int flags; +{ + + panic("Must use vme_dmamem_alloc() in place of bus_dmamem_alloc()\n"); +} + +/* ARGSUSED */ +void +mvmebus_dummy_dmamem_free(t, segs, nsegs) + bus_dma_tag_t t; + bus_dma_segment_t *segs; + int nsegs; +{ + + panic("Must use vme_dmamem_free() in place of bus_dmamem_free()"); +} +#endif + +/* ARGSUSED */ +int +mvmebus_dmamem_alloc(vsc, len, am, datasize, swap, segs, nsegs, rsegs, flags) + void *vsc; + vme_size_t len; + vme_am_t am; + vme_datasize_t datasize; + vme_swap_t swap; + bus_dma_segment_t *segs; + int nsegs; + int *rsegs; + int flags; +{ + extern paddr_t avail_start; + struct mvmebus_softc *sc = vsc; + struct mvmebus_range *vr; + bus_addr_t low, high; + bus_size_t bound; + vme_am_t cap; + int i; + + cap = MVMEBUS_AM2CAP(am); + am &= VME_AM_ADRSIZEMASK; + + /* + * Find a slave mapping in the requested VMEbus address space. + */ + for (i = 0, vr = sc->sc_slaves; i < sc->sc_nslaves; i++, vr++) { + if (vr->vr_am == MVMEBUS_AM_DISABLED) + continue; + + if (i == 0 && (flags & BUS_DMA_ONBOARD_RAM) != 0) + continue; + + if (am == (vr->vr_am & VME_AM_ADRSIZEMASK) && + cap == (vr->vr_am & cap) && datasize <= vr->vr_datasize && + len <= (vr->vr_vmeend - vr->vr_vmestart)) + break; + } + if (i == sc->sc_nslaves) + return (EINVAL); + + /* + * Set up the constraints so we can allocate physical memory which + * is visible in the requested address space + */ + low = max(vr->vr_locstart, avail_start); + high = vr->vr_locstart + (vr->vr_vmeend - vr->vr_vmestart) + 1; + bound = (bus_size_t) vr->vr_mask + 1; + + /* + * Allocate physical memory. + * + * Note: This fills in the segments with cpu-relative physical + * addresses. A further call to bus_dmamap_load_raw() (with a + * dma map which specifies the same VMEbus address space and + * constraints as the call to here) must be made. The segments + * of the dma map will then contain VMEbus-relative physical + * addresses of the memory allocated here. + */ + return _bus_dmamem_alloc_common(sc->sc_dmat, low, high, + len, 0, bound, segs, nsegs, rsegs, flags); +} + +void +mvmebus_dmamem_free(vsc, segs, nsegs) + void *vsc; + bus_dma_segment_t *segs; + int nsegs; +{ + struct mvmebus_softc *sc = vsc; + + bus_dmamem_free(sc->sc_dmat, segs, nsegs); +} + +int +mvmebus_dmamem_map(t, segs, nsegs, size, kvap, flags) + bus_dma_tag_t t; + bus_dma_segment_t *segs; + int nsegs; + size_t size; + caddr_t *kvap; + int flags; +{ + struct mvmebus_softc *sc = t->_cookie; + + return bus_dmamem_map(sc->sc_dmat, segs, nsegs, size, kvap, flags); +} + +void +mvmebus_dmamem_unmap(t, kva, size) + bus_dma_tag_t t; + caddr_t kva; + size_t size; +{ + struct mvmebus_softc *sc = t->_cookie; + + bus_dmamem_unmap(sc->sc_dmat, kva, size); +} + +paddr_t +mvmebus_dmamem_mmap(t, segs, nsegs, offset, prot, flags) + bus_dma_tag_t t; + bus_dma_segment_t *segs; + int nsegs; + off_t offset; + int prot; + int flags; +{ + struct mvmebus_softc *sc = t->_cookie; + + return bus_dmamem_mmap(sc->sc_dmat, segs, nsegs, offset, prot, flags); +} + +#ifdef DEBUG +static const char * +mvmebus_mod_string(addr, len, am, ds) + vme_addr_t addr; + vme_size_t len; + vme_am_t am; + vme_datasize_t ds; +{ + static const char *mode[] = {"BLT64)", "DATA)", "PROG)", "BLT32)"}; + static const char *dsiz[] = {"(", "(D8,", "(D16,", "(D16-D8,", + "(D32,", "(D32,D8,", "(D32-D16,", "(D32-D8,"}; + static char mstring[40]; + char *fmt; + + switch (am & VME_AM_ADRSIZEMASK) { + case VME_AM_A32: + fmt = "A32:%08x-%08x "; + break; + + case VME_AM_A24: + fmt = "A24:%06x-%06x "; + break; + + case VME_AM_A16: + fmt = "A16:%04x-%04x "; + break; + + case VME_AM_USERDEF: + fmt = "USR:%08x-%08x "; + break; + } + + sprintf(mstring, fmt, addr, addr + len - 1); + strcat(mstring, dsiz[ds & 0x7]); + + if (MVMEBUS_AM_HAS_CAP(am)) { + if (am & MVMEBUS_AM_CAP_DATA) + strcat(mstring, "D"); + if (am & MVMEBUS_AM_CAP_PROG) + strcat(mstring, "P"); + if (am & MVMEBUS_AM_CAP_USER) + strcat(mstring, "U"); + if (am & MVMEBUS_AM_CAP_SUPER) + strcat(mstring, "S"); + if (am & MVMEBUS_AM_CAP_BLK) + strcat(mstring, "B"); + if (am & MVMEBUS_AM_CAP_BLKD64) + strcat(mstring, "6"); + strcat(mstring, ")"); + } else { + strcat(mstring, ((am & VME_AM_PRIVMASK) == VME_AM_USER) ? + "USER," : "SUPER,"); + strcat(mstring, mode[am & VME_AM_MODEMASK]); + } + + return (mstring); +} +#endif diff --git a/sys/dev/mvme/mvmebus.h b/sys/dev/mvme/mvmebus.h new file mode 100644 index 00000000000..e2c0c5e6e72 --- /dev/null +++ b/sys/dev/mvme/mvmebus.h @@ -0,0 +1,161 @@ +/* $NetBSD: mvmebus.h,v 1.1 2002/02/12 20:38:48 scw Exp $ */ + +/*- + * Copyright (c) 2000, 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Steve C. Woodford. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#ifndef _MVME_MVMEBUS_H +#define _MVME_MVMEBUS_H + +/* + * VMEbus master and slave windows are described using + * instances of this structure. + * + * The chip-specific code records the details of the available mappings + * for the MVME board. + */ +struct mvmebus_range { + vme_am_t vr_am; /* Address modifier (A16, A24, A32) */ + vme_datasize_t vr_datasize; /* Datasize: logical OR of D8,D16,D32 */ + paddr_t vr_locstart; /* CPU-relative address of mapping */ + paddr_t vr_mask; /* Mask to apply to this mapping */ + vme_addr_t vr_vmestart; /* VMEbus start address */ + vme_addr_t vr_vmeend; /* VMEbus end address */ +}; + +/* Assigned to vr_am to specify the mapping is not valid */ +#define MVMEBUS_AM_DISABLED ((vme_am_t)-1) + +/* For slave mappings, these specify the slave's capabilities */ +#define MVMEBUS_AM_CAP_DATA 0x0100 +#define MVMEBUS_AM_CAP_PROG 0x0200 +#define MVMEBUS_AM_CAP_BLK 0x0400 +#define MVMEBUS_AM_CAP_BLKD64 0x0800 +#define MVMEBUS_AM_CAP_USER 0x1000 +#define MVMEBUS_AM_CAP_SUPER 0x2000 +#define MVMEBUS_AM_HAS_CAP(x) (((x) & 0x3f00) != 0) + +#define MVMEBUS_AM2CAP(am) (_mvmebus_am_cap[((am) & \ + (VME_AM_MODEMASK | VME_AM_PRIVMASK))]) + + +/* + * This records VMEbus-specific details of a region of bus_space_map'd + * VMEbus address space. + */ +struct mvmebus_mapresc { + bus_space_handle_t mr_handle; + bus_addr_t mr_addr; + bus_size_t mr_size; + vme_am_t mr_am; + vme_datasize_t mr_datasize; + int mr_range; +}; + + +/* + * This records the VMEbus-specific details of a region of phyisical + * memory accessible through a VMEbus slave map. + */ +struct mvmebus_dmamap { + vme_am_t vm_am; + vme_datasize_t vm_datasize; + vme_swap_t vm_swap; + struct mvmebus_range *vm_slave; +}; + + +struct mvmebus_softc { + struct device sc_dev; + bus_space_tag_t sc_bust; + bus_dma_tag_t sc_dmat; + short sc_irqref[8]; + void *sc_chip; + int sc_nmasters; + struct mvmebus_range *sc_masters; + int sc_nslaves; + struct mvmebus_range *sc_slaves; + struct evcnt sc_evcnt[7]; + void (*sc_intr_establish)(void *, int, int, int, int, + int (*)(void *), void *, struct evcnt *); + void (*sc_intr_disestablish)(void *, int, int, int, + struct evcnt *); + struct vme_chipset_tag sc_vct; + struct mvme68k_bus_dma_tag sc_mvmedmat; +}; + + +void mvmebus_attach(struct mvmebus_softc *); +int mvmebus_map(void *, vme_addr_t, vme_size_t, vme_am_t, + vme_datasize_t, vme_swap_t, bus_space_tag_t *, + bus_space_handle_t *, vme_mapresc_t *); +void mvmebus_unmap(void *, vme_mapresc_t); +int mvmebus_probe(void *, vme_addr_t, vme_size_t, + vme_am_t, vme_datasize_t, + int (*) (void *, bus_space_tag_t, bus_space_handle_t), void *arg); +int mvmebus_intmap(void *, int, int, vme_intr_handle_t *); +const struct evcnt *mvmebus_intr_evcnt(void *, vme_intr_handle_t); +void * mvmebus_intr_establish(void *, vme_intr_handle_t, int, + int (*) (void *), void *); +void mvmebus_intr_disestablish(void *, vme_intr_handle_t); + +int mvmebus_dmamap_create(void *, vme_size_t, vme_am_t, vme_datasize_t, + vme_swap_t, int, vme_size_t, vme_addr_t, int, bus_dmamap_t *); +void mvmebus_dmamap_destroy(void *, bus_dmamap_t); +int mvmebus_dmamap_load(bus_dma_tag_t, bus_dmamap_t, + void *, bus_size_t, struct proc *, int); +int mvmebus_dmamap_load_mbuf(bus_dma_tag_t, + bus_dmamap_t, struct mbuf *, int); +int mvmebus_dmamap_load_uio(bus_dma_tag_t, + bus_dmamap_t, struct uio *, int); +int mvmebus_dmamap_load_raw(bus_dma_tag_t, + bus_dmamap_t, bus_dma_segment_t *, int, bus_size_t, int); +void mvmebus_dmamap_unload(bus_dma_tag_t, bus_dmamap_t); +void mvmebus_dmamap_sync(bus_dma_tag_t, bus_dmamap_t, bus_addr_t, + bus_size_t, int); + +int mvmebus_dmamem_alloc(void *, vme_size_t, vme_am_t, vme_datasize_t, + vme_swap_t, bus_dma_segment_t *, int, int *, int); +void mvmebus_dmamem_free(void *, bus_dma_segment_t *, int); +int mvmebus_dmamem_map(bus_dma_tag_t, bus_dma_segment_t *, int, + size_t, caddr_t *, int); +void mvmebus_dmamem_unmap(bus_dma_tag_t, caddr_t, size_t); +paddr_t mvmebus_dmamem_mmap(bus_dma_tag_t, bus_dma_segment_t *, int, + off_t, int, int); + +extern vme_am_t _mvmebus_am_cap[]; +extern const char *mvmebus_irq_name[]; + +#endif /* _MVME_MVMEBUS_H */ diff --git a/sys/dev/mvme/osiop_pcctwo.c b/sys/dev/mvme/osiop_pcctwo.c new file mode 100644 index 00000000000..f959f35faf9 --- /dev/null +++ b/sys/dev/mvme/osiop_pcctwo.c @@ -0,0 +1,191 @@ +/* $NetBSD: osiop_pcctwo.c,v 1.1 2002/02/12 20:38:48 scw Exp $ */ + +/*- + * Copyright (c) 1999, 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Steve C. Woodford. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* + * Front-end attachment code for the ncr53c710 SCSI controller + * on mvme68k/mvme88k boards. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/device.h> + +#include <dev/scsipi/scsi_all.h> +#include <dev/scsipi/scsipi_all.h> +#include <dev/scsipi/scsiconf.h> + +#include <machine/cpu.h> +#include <machine/bus.h> +#include <machine/autoconf.h> + +#include <dev/ic/osiopreg.h> +#include <dev/ic/osiopvar.h> + +#include <dev/mvme/pcctworeg.h> +#include <dev/mvme/pcctwovar.h> + + +int osiop_pcctwo_match __P((struct device *, struct cfdata *, void *)); +void osiop_pcctwo_attach __P((struct device *, struct device *, void *)); + +struct osiop_pcctwo_softc { + struct osiop_softc sc_osiop; + struct evcnt sc_evcnt; +}; + +struct cfattach osiop_pcctwo_ca = { + sizeof(struct osiop_pcctwo_softc), osiop_pcctwo_match, + osiop_pcctwo_attach +}; + +static int osiop_pcctwo_intr __P((void *)); + +extern struct cfdriver osiop_cd; + +/* ARGSUSED */ +int +osiop_pcctwo_match(parent, cf, args) + struct device *parent; + struct cfdata *cf; + void *args; +{ + struct pcctwo_attach_args *pa; + + pa = args; + + if (strcmp(pa->pa_name, osiop_cd.cd_name)) + return (0); + + pa->pa_ipl = cf->pcctwocf_ipl; + + return (1); +} + +/* ARGSUSED */ +void +osiop_pcctwo_attach(parent, self, args) + struct device *parent; + struct device *self; + void *args; +{ + struct pcctwo_attach_args *pa; + struct osiop_pcctwo_softc *sc; + int clk, ctest7; + + pa = (struct pcctwo_attach_args *) args; + sc = (struct osiop_pcctwo_softc *) self; + + /* + * On the '17x the siop's clock is the same as the cpu clock. + * On the other boards, the siop runs at twice the cpu clock. + * Also, the 17x cannot do proper bus-snooping (the 68060 is + * lame in this repspect) so don't enable it on that board. + */ +#ifdef MVME68K + if (machineid == MVME_172 || machineid == MVME_177) { + clk = cpuspeed; + ctest7 = 0; + } else { + clk = cpuspeed * 2; + ctest7 = OSIOP_CTEST7_SC0; + } +#else +#error Set up siop clock speed for mvme187 +#endif + + sc->sc_osiop.sc_bst = pa->pa_bust; + sc->sc_osiop.sc_dmat = pa->pa_dmat; + (void) bus_space_map(sc->sc_osiop.sc_bst, pa->pa_offset, OSIOP_NREGS, + 0, &sc->sc_osiop.sc_reg); + + sc->sc_osiop.sc_clock_freq = clk; + sc->sc_osiop.sc_ctest7 = ctest7 | OSIOP_CTEST7_TT1; + sc->sc_osiop.sc_dcntl = OSIOP_DCNTL_EA; + sc->sc_osiop.sc_id = 7; /* XXX: Could use NVRAM setting */ + + /* Attach main MI driver */ + osiop_attach(&sc->sc_osiop); + + /* Register the event counter */ + evcnt_attach_dynamic(&sc->sc_evcnt, EVCNT_TYPE_INTR, + pcctwointr_evcnt(pa->pa_ipl), "disk", sc->sc_osiop.sc_dev.dv_xname); + + /* Hook the chip's interrupt */ + pcctwointr_establish(PCCTWOV_SCSI, osiop_pcctwo_intr, pa->pa_ipl, sc, + &sc->sc_evcnt); +} + +static int +osiop_pcctwo_intr(arg) + void *arg; +{ + struct osiop_pcctwo_softc *sc = (struct osiop_pcctwo_softc *) arg; + u_char istat; + + /* + * Catch any errors which can happen when the SIOP is + * local bus master... + */ + istat = pcc2_reg_read(sys_pcctwo, PCC2REG_SCSI_ERR_STATUS); + if ((istat & PCCTWO_ERR_SR_MASK) != 0) { + printf("%s: Local bus error: 0x%02x\n", + sc->sc_osiop.sc_dev.dv_xname, istat); + istat |= PCCTWO_ERR_SR_SCLR; + pcc2_reg_write(sys_pcctwo, PCC2REG_SCSI_ERR_STATUS, istat); + } + + /* This is potentially nasty, since the IRQ is level triggered... */ + if (sc->sc_osiop.sc_flags & OSIOP_INTSOFF) + return (0); + + istat = osiop_read_1(&sc->sc_osiop, OSIOP_ISTAT); + + if ((istat & (OSIOP_ISTAT_SIP | OSIOP_ISTAT_DIP)) == 0) + return (0); + + /* Save interrupt details for the back-end interrupt handler */ + sc->sc_osiop.sc_sstat0 = osiop_read_1(&sc->sc_osiop, OSIOP_SSTAT0); + sc->sc_osiop.sc_istat = istat; + sc->sc_osiop.sc_dstat = osiop_read_1(&sc->sc_osiop, OSIOP_DSTAT); + + /* Deal with the interrupt */ + osiop_intr(&sc->sc_osiop); + + return (1); +} diff --git a/sys/dev/mvme/pcctwo.c b/sys/dev/mvme/pcctwo.c new file mode 100644 index 00000000000..13c1fe9a2ab --- /dev/null +++ b/sys/dev/mvme/pcctwo.c @@ -0,0 +1,204 @@ +/* $NetBSD: pcctwo.c,v 1.1 2002/02/12 20:38:49 scw Exp $ */ + +/*- + * Copyright (c) 1999, 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Steve C. Woodford. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* + * PCCchip2 and MCchip Driver + */ + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/device.h> + +#include <machine/cpu.h> +#include <machine/bus.h> + +#include <dev/mvme/pcctworeg.h> +#include <dev/mvme/pcctwovar.h> + +/* + * Global Pointer to the PCCChip2/MCchip soft state, and chip ID + */ +struct pcctwo_softc *sys_pcctwo; + +int pcctwoprint(void *, const char *); + + +/* ARGSUSED */ +void +pcctwo_init(sc, pd, devoff) + struct pcctwo_softc *sc; + struct pcctwo_device *pd; + int devoff; +{ + struct pcctwo_attach_args npa; + u_int8_t cid; + + /* + * Fix up the vector base for PCCChip2 Interrupts + */ + pcc2_reg_write(sc, PCC2REG_VECTOR_BASE, sc->sc_vecbase); + + /* + * Enable PCCChip2 Interrupts + */ + pcc2_reg_write(sc, PCC2REG_GENERAL_CONTROL, + pcc2_reg_read(sc, PCC2REG_GENERAL_CONTROL) | PCCTWO_GEN_CTRL_MIEN); + + /* What are we? */ + cid = pcc2_reg_read(sc, PCC2REG_CHIP_ID); + + /* + * Announce ourselves to the world in general + */ + if (cid == PCCTWO_CHIP_ID_PCC2) + printf(": Peripheral Channel Controller (PCCchip2), Rev %d\n", + pcc2_reg_read(sc, PCC2REG_CHIP_REVISION)); + else + if (cid == PCCTWO_CHIP_ID_MCCHIP) + printf(": Memory Controller ASIC (MCchip), Rev %d\n", + pcc2_reg_read(sc, PCC2REG_CHIP_REVISION)); + + /* + * Attach configured children. + */ + npa._pa_base = devoff; + while (pd->pcc_name != NULL) { + /* + * Note that IPL is filled in by match function. + */ + npa.pa_name = pd->pcc_name; + npa.pa_ipl = -1; + npa.pa_dmat = sc->sc_dmat; + npa.pa_bust = sc->sc_bust; + npa.pa_offset = pd->pcc_offset + devoff; + pd++; + + /* Attach the device if configured. */ + (void) config_found(&sc->sc_dev, &npa, pcctwoprint); + } +} + +int +pcctwoprint(aux, cp) + void *aux; + const char *cp; +{ + struct pcctwo_attach_args *pa; + + pa = aux; + + if (cp) + printf("%s at %s", pa->pa_name, cp); + + printf(" offset 0x%lx", pa->pa_offset - pa->_pa_base); + if (pa->pa_ipl != -1) + printf(" ipl %d", pa->pa_ipl); + + return (UNCONF); +} + +/* + * pcctwointr_establish: Establish PCCChip2 Interrupt + */ +void +pcctwointr_establish(vec, hand, lvl, arg, evcnt) + int vec; + int (*hand) __P((void *)), lvl; + void *arg; + struct evcnt *evcnt; +{ + int vec2icsr; + +#ifdef DEBUG + if (vec < 0 || vec >= PCCTWOV_MAX) { + printf("pcctwo: illegal vector offset: 0x%x\n", vec); + panic("pcctwointr_establish"); + } + if (lvl < 1 || lvl > 7) { + printf("pcctwo: illegal interrupt level: %d\n", lvl); + panic("pcctwointr_establish"); + } + if (sys_pcctwo->sc_vec2icsr[vec] == -1) { + printf("pcctwo: unsupported vector: %d\n", vec); + panic("pcctwointr_establish"); + } +#endif + + vec2icsr = sys_pcctwo->sc_vec2icsr[vec]; + pcc2_reg_write(sys_pcctwo, VEC2ICSR_REG(vec2icsr), 0); + + /* Hook the interrupt */ + (*sys_pcctwo->sc_isrlink)(sys_pcctwo->sc_isrcookie, hand, arg, + lvl, vec + sys_pcctwo->sc_vecbase, evcnt); + + /* Enable it in hardware */ + pcc2_reg_write(sys_pcctwo, VEC2ICSR_REG(vec2icsr), + VEC2ICSR_INIT(vec2icsr) | lvl); +} + +void +pcctwointr_disestablish(vec) + int vec; +{ + +#ifdef DEBUG + if (vec < 0 || vec >= PCCTWOV_MAX) { + printf("pcctwo: illegal vector offset: 0x%x\n", vec); + panic("pcctwointr_disestablish"); + } + if (sys_pcctwo->sc_vec2icsr[vec] == -1) { + printf("pcctwo: unsupported vector: %d\n", vec); + panic("pcctwointr_establish"); + } +#endif + + /* Disable it in hardware */ + pcc2_reg_write(sys_pcctwo, sys_pcctwo->sc_vec2icsr[vec], 0); + + (*sys_pcctwo->sc_isrunlink)(sys_pcctwo->sc_isrcookie, + vec + sys_pcctwo->sc_vecbase); +} + +struct evcnt * +pcctwointr_evcnt(lev) + int lev; +{ + + return ((*sys_pcctwo->sc_isrevcnt)(sys_pcctwo->sc_isrcookie, lev)); +} diff --git a/sys/dev/mvme/pcctworeg.h b/sys/dev/mvme/pcctworeg.h new file mode 100644 index 00000000000..d644ca583e1 --- /dev/null +++ b/sys/dev/mvme/pcctworeg.h @@ -0,0 +1,316 @@ +/* $NetBSD: pcctworeg.h,v 1.1 2002/02/12 20:38:49 scw Exp $ */ + +/*- + * Copyright (c) 1999, 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Steve C. Woodford + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* + * Register definitions for the PCCchip2 device, and offset to the + * various subordinate devices which hang off it. + */ +#ifndef _MVME_PCCTWOREG_H +#define _MVME_PCCTWOREG_H + +/* + * Offsets to the various devices which hang off the PCCChip2. + * Note that these are offsets from the base of the PCCChip2's + * own registers. + */ +#define PCCTWO_REG_OFF 0x00000 /* Offset of PCCChip2's own registers */ +#define PCCTWO_LPT_OFF 0x00000 /* Offset of parallel port registers */ +#define PCCTWO_SCC_OFF 0x03000 /* Offset of CD2401 Serial Comms chip */ +#define PCCTWO_IE_OFF 0x04000 /* Offset of 82596 LAN controller */ +#define PCCTWO_NCRSC_OFF 0x05000 /* Offset of NCR53C710 SCSI chip */ + +/* + * The two devices on mvme1[67]2's MCchip + */ +#define MCCHIP_ZS0_OFF 0x03000 +#define MCCHIP_ZS1_OFF 0x03800 + +/* + * This is needed to figure out the boot device. + * (The physical address of the boot device's registers are passed in + * from the Boot ROM) + */ +#define PCCTWO_PADDR(off) ((void *)(0xfff42000u + (off))) + + +/* + * The layout of the PCCchip2's Registers. + * Each one is 8-bits wide, unless otherwise indicated. + */ +#define PCC2REG_CHIP_ID 0x00 /* Chip ID */ +#define PCC2REG_CHIP_REVISION 0x01 /* Chip Revision */ +#define PCC2REG_GENERAL_CONTROL 0x02 /* General Control */ +#define PCC2REG_VECTOR_BASE 0x03 /* Vector Base */ +#define PCC2REG_TIMER1_COMPARE 0x04 /* Tick Timer 1 Compare (32-bit) */ +#define PCC2REG_TIMER1_COUNTER 0x08 /* Tick Timer 1 Counter (32-bit) */ +#define PCC2REG_TIMER2_COMPARE 0x0c /* Tick Timer 2 Compare (32-bit) */ +#define PCC2REG_TIMER2_COUNTER 0x10 /* Tick Timer 2 Counter (32-bit) */ +#define PCC2REG_PRESCALE_COUNT 0x14 /* Prescaler Count */ +#define PCC2REG_PRESCALE_ADJUST 0x15 /* Prescaler Clock Adjust */ +#define PCC2REG_TIMER2_CONTROL 0x16 /* Tick Timer 2 Control */ +#define PCC2REG_TIMER1_CONTROL 0x17 /* Tick Timer 1 Control */ +#define PCC2REG_GPIO_ICSR 0x18 /* GP Input Interrupt Control */ +#define PCC2REG_GPIO_CONTROL 0x19 /* GP Input/Output Control */ +#define PCC2REG_TIMER2_ICSR 0x1a /* Tick Timer 2 Interrupt Control */ +#define PCC2REG_TIMER1_ICSR 0x1b /* Tick Timer 1 Interrupt Control */ +#define PCC2REG_SCC_ERR_STATUS 0x1c /* SCC Error Status */ +#define PCC2REG_SCC_MODEM_ICSR 0x1d /* SCC Modem Interrupt Control */ +#define PCC2REG_SCC_TX_ICSR 0x1e /* SCC Transmit Interrupt Control */ +#define PCC2REG_SCC_RX_ICSR 0x1f /* SCC Receive Interrupt Control */ +#define PCC2REG_SCC_MODEM_PIACK 0x23 /* SCC Modem PIACK */ +#define PCC2REG_SCC_TX_PIACK 0x25 /* SCC Transmit PIACK */ +#define PCC2REG_SCC_RX_PIACK 0x27 /* SCC Receive PIACK */ +#define PCC2REG_ETH_ERR_STATUS 0x28 /* LANC Error Status */ +#define PCC2REG_ETH_ICSR 0x2a /* LANC Interrupt Control */ +#define PCC2REG_ETH_BERR_STATUS 0x2b /* LANC Bus Error Interrupt Ctrl */ +#define PCC2REG_SCSI_ERR_STATUS 0x2c /* SCSI Error Status */ +#define PCC2REG_SCSI_ICSR 0x2f /* SCSI Interrupt Control */ +#define PCC2REG_PRT_ACK_ICSR 0x30 /* Printer ACK Interrupt Control */ +#define PCC2REG_PRT_FAULT_ICSR 0x31 /* Printer FAULT Interrupt Ctrl */ +#define PCC2REG_PRT_SEL_ICSR 0x32 /* Printer SEL Interrupt Control */ +#define PCC2REG_PRT_PE_ICSR 0x33 /* Printer PE Interrupt Control */ +#define PCC2REG_PRT_BUSY_ICSR 0x34 /* Printer BUSY Interrupt Control */ +#define PCC2REG_PRT_INPUT_STATUS 0x36 /* Printer Input Status */ +#define PCC2REG_PRT_CONTROL 0x37 /* Printer Port Control */ +#define PCC2REG_CHIP_SPEED 0x38 /* Chip Speed (16-bit) */ +#define PCC2REG_PRT_DATA 0x3a /* Printer Data (16-bit) */ +#define PCC2REG_IRQ_LEVEL 0x3e /* Interrupt Priority Level */ +#define PCC2REG_IRQ_MASK 0x3f /* Interrupt Mask */ + +/* + * Additions to the registers for the MCChip. Some of these overlap with + * the PCCchip2's registers, but only where hardware is not present, eg. + * the printer registers. + */ +#define MCCHIPREG_TIMER4_ICSR 0x18 /* Tick timer 4 interrupt control */ +#define MCCHIPREG_TIMER3_ICSR 0x19 /* Tick timer 4 interrupt control */ +#define MCCHIPREG_PARERR_ICSR 0x1c /* Parity error interrupt control */ +#define MCCHIPREG_SCC_ICSR 0x1d /* ZS-85230 interrupt control */ +#define MCCHIPREG_TIMER4_CTRL 0x1e /* Tick timer 4 control */ +#define MCCHIPREG_TIMER3_CTRL 0x1f /* Tick timer 3 control */ +#define MCCHIPREG_DRAM_BAR 0x20 /* DRAM Base Address (16-bits) */ +#define MCCHIPREG_SRAM_BAR 0x22 /* SRAM Base Address (16-bits) */ +#define MCCHIPREG_DRAM_SIZE 0x24 /* DRAM Size */ +#define MCCHIPREG_RAM_OPTIONS 0x25 /* DRAM/SRAM Options */ +#define MCCHIPREG_SRAM_SIZE 0x26 /* SRAM Size */ +#define MCCHIPREG_GP_INPUTS 0x2d /* General Purpose Inputs */ +#define MCCHIPREG_162_VERSION 0x2e /* MVME162-LX Series Version */ +#define MCCHIPREG_TIMER3_COMP 0x30 /* Tick Timer 3 Compare (32-bit) */ +#define MCCHIPREG_TIMER3_CNTR 0x34 /* Tick Timer 3 Counter (32-bit) */ +#define MCCHIPREG_TIMER4_COMP 0x38 /* Tick Timer 4 Compare (32-bit) */ +#define MCCHIPREG_TIMER4_CNTR 0x3c /* Tick Timer 4 Counter (32-bit) */ +#define MCCHIPREG_BUS_CLOCK 0x40 /* Bus Clock */ +#define MCCHIPREG_EPROM_TIMING 0x41 /* EPROM Access Time Control */ +#define MCCHIPREG_FLASH_TIMING 0x42 /* FLASH Access Time Control */ +#define MCCHIPREG_ABORT_ICSR 0x43 /* ABORT Switch Interrupt Control */ +#define MCCHIPREG_RESET_CONTROL 0x44 /* Reset Switch Control */ +#define MCCHIPREG_WDOG_CONTROL 0x45 /* Watchdog Timer Control */ +#define MCCHIPREG_TIMEBASE_SEL 0x46 /* Access & Watchdog Timebase Select */ +#define MCCHIPREG_DRAM_CONTROL 0x48 /* Parity DRAM Control */ +#define MCCHIPREG_MPU_STATUS 0x4a /* MPU Status */ +#define MCCHIPREG_PRESCALER 0x4c /* Prescaler Count Register (32-bits) */ + +/* + * PCCchip2's register size is 0x40. MCchip's is 0x50. Plump for the latter. + */ +#define PCC2REG_SIZE 0x50 + +/* + * Convenience macroes for accessing the PCCChip2's registers + * through bus_space. + */ +#define pcc2_reg_read(sc,r) \ + bus_space_read_1((sc)->sc_bust, (sc)->sc_bush, (r)) +#define pcc2_reg_read16(sc,r) \ + bus_space_read_2((sc)->sc_bust, (sc)->sc_bush, (r)) +#define pcc2_reg_read32(sc,r) \ + bus_space_read_4((sc)->sc_bust, (sc)->sc_bush, (r)) +#define pcc2_reg_write(sc,r,v) \ + bus_space_write_1((sc)->sc_bust, (sc)->sc_bush, (r), (v)) +#define pcc2_reg_write16(sc,r,v) \ + bus_space_write_2((sc)->sc_bust, (sc)->sc_bush, (r), (v)) +#define pcc2_reg_write32(sc,r,v) \ + bus_space_write_4((sc)->sc_bust, (sc)->sc_bush, (r), (v)) + +/* + * We use the interrupt vector bases suggested in the Motorola Docs... + * The first is written to the PCCChip2 for interrupt sources under + * its control. The second is written to the CD2401's Local Interrupt + * Vector Register. Thus, we don't use the Auto-Vector facilities + * for the CD2401, as recommended in the PCCChip2 Programmer's Guide. + * The third is used as a base for the ZS85230 serial chips on mvme162. + */ +#define PCCTWO_VECBASE 0x50 +#define PCCTWO_SCC_VECBASE 0x5c +#define MCCHIP_ZS_VECBASE 0x5c + +/* + * PCCchip2 Vector Encoding (Offsets from PCCTWO_VECBASE) + * The order 0x0 -> 0xf also indicates priority, with 0x0 lowest. + */ +#define PCCTWOV_PRT_BUSY 0x0 /* Printer Port 'BSY' */ +#define PCCTWOV_PRT_PE 0x1 /* Printer Port 'PE' (Paper Empty) */ +#define PCCTWOV_PRT_SELECT 0x2 /* Printer Port 'SELECT' */ +#define PCCTWOV_PRT_FAULT 0x3 /* Printer Port 'FAULT' */ +#define PCCTWOV_PRT_ACK 0x4 /* Printer Port 'ACK' */ +#define PCCTWOV_SCSI 0x5 /* SCSI Interrupt */ +#define PCCTWOV_LANC_ERR 0x6 /* LAN Controller Error */ +#define PCCTWOV_LANC_IRQ 0x7 /* LAN Controller Interrupt */ +#define PCCTWOV_TIMER2 0x8 /* Tick Timer 2 Interrupt */ +#define PCCTWOV_TIMER1 0x9 /* Tick Timer 1 Interrupt */ +#define PCCTWOV_GPIO 0xa /* General Purpose Input Interrupt */ +#define PCCTWOV_SCC_RX_EXCEP 0xc /* SCC Receive Exception */ +#define PCCTWOV_SCC_MODEM 0xd /* SCC Modem (Non-Auto-vector mode) */ +#define PCCTWOV_SCC_TX 0xe /* SCC Tx (Non-Auto-vector mode) */ +#define PCCTWOV_SCC_RX 0xf /* SCC Rx (Non-Auto-vector mode) */ +#define PCCTWOV_MAX 16 + +/* + * MCchip-specific Vector Encoding (Offsets from PCCTWO_VECBASE) + */ +#define MCCHIPV_TIMER4 0x3 /* Tick Timer 4 Interrupt */ +#define MCCHIPV_TIMER3 0x4 /* Tick Timer 3 Interrupt */ +#define MCCHIPV_PARITY_ERR 0xb /* Parity DRAM Error Exception */ +#define MCCHIPV_ZS0 0xc /* First ZS85230 Interrupt Vector */ +#define MCCHIPV_ZS1 0xc /* Second ZS85230 Interrupt Vector */ +#define MCCHIPV_ABORT 0xe /* Abort Switch */ + +/* + * How to identify the PCCchip2 from an MCchip + */ +#define PCCTWO_CHIP_ID_PCC2 0x20 +#define PCCTWO_CHIP_ID_MCCHIP 0x84 + + +/* + * Bit Values for the General Control Register (PCC2REG_GENERAL_CONTROL) + */ +#define PCCTWO_GEN_CTRL_FAST (1u<<0) /* BBRAM Speed Control */ +#define PCCTWO_GEN_CTRL_MIEN (1u<<1) /* Master Interrupt Enable */ +#define PCCTWO_GEN_CTRL_C040 (1u<<2) /* Set when CPU is mc68k family */ +#define PCCTWO_GEN_CTRL_DR0 (1u<<3) /* Download ROM at 0 (mvme166 only) */ + + +/* + * Calculate the Prescaler Adjust value for a given + * value of BCLK in MHz. (PCC2REG_PRESCALE_ADJUST) + */ +#define PCCTWO_PRES_ADJ(mhz) (256 - (mhz)) + + +/* + * Calculate the Tick Timer Compare register value for + * a given number of micro-seconds. With the PCCChip2, + * this is simple since the Tick Counters already have + * a 1uS period. (PCC2REG_TIMER[12]_COMPARE) + */ +#define PCCTWO_US2LIM(us) (us) +#define PCCTWO_LIM2US(lim) (lim) + +/* + * The Tick Timer Control Registers (PCC2REG_TIMER[12]_CONTROL) + */ +#define PCCTWO_TT_CTRL_CEN (1u<<0) /* Counter Enable */ +#define PCCTWO_TT_CTRL_COC (1u<<1) /* Clear On Compare */ +#define PCCTWO_TT_CTRL_COVF (1u<<2) /* Clear Overflow Counter */ +#define PCCTWO_TT_CTRL_OVF(r) ((r)>>4)/* Value of the Overflow Counter */ + + +/* + * All the Interrupt Control Registers (PCC2REG_*_ICSR) on the PCCChip2 + * mostly share the same basic layout. These are defined as follows: + */ +#define PCCTWO_ICR_LEVEL_MASK 0x7 /* Mask for the interrupt level */ +#define PCCTWO_ICR_ICLR (1u<<3) /* Clear Int. (edge-sensitive mode) */ +#define PCCTWO_ICR_AVEC (1u<<3) /* Enable Auto-Vector Mode */ +#define PCCTWO_ICR_IEN (1u<<4) /* Interrupt Enable */ +#define PCCTWO_ICR_INT (1u<<5) /* Interrupt Active */ +#define PCCTWO_ICR_LEVEL (0u<<6) /* Level Triggered */ +#define PCCTWO_ICR_EDGE (1u<<6) /* Edge Triggered */ +#define PCCTWO_ICR_RISE_HIGH (0u<<7) /* Polarity: Rising Edge or Hi Level */ +#define PCCTWO_ICR_FALL_LOW (1u<<7) /* Polarity: Falling Edge or Lo Level */ +#define PCCTWO_ICR_SC_RD(r) ((r)>>6)/* Get Snoop Control Bits */ +#define PCCTWO_ICR_SC_WR(r) ((r)<<6)/* Write Snoop Control Bits */ + + + +/* + * Most of the Error Status Registers (PCC2REG_*_ERR_STATUS) mostly + * follow the same layout. These error registers are used when a + * device (eg. SCC, LANC) is mastering the PCCChip2's local bus (for + * example, performing a DMA) and some error occurs. The bits are + * defined as follows: + */ +#define PCCTWO_ERR_SR_SCLR (1u<<0) /* Clear Error Status */ +#define PCCTWO_ERR_SR_LTO (1u<<1) /* Local Bus Timeout */ +#define PCCTWO_ERR_SR_EXT (1u<<2) /* External (VMEbus) Error */ +#define PCCTWO_ERR_SR_PRTY (1u<<3) /* DRAM Parity Error */ +#define PCCTWO_ERR_SR_RTRY (1u<<4) /* Retry Required */ +#define PCCTWO_ERR_SR_MASK 0x0Eu + + +/* + * General Purpose Input/Output Pin Control Register + * (PCC2REG_GPIO_CONTROL) + */ +#define PCCTWO_GPIO_CTRL_GPO (1u<<0) /* Controls the GP Output Pin */ +#define PCCTWO_GPIO_CTRL_GPOE (1u<<1) /* General Purpose Output Enable */ +#define PCCTWO_GPIO_CTRL_GPI (1u<<3) /* The current state of the GP Input */ + + +/* + * Printer Input Status Register (PCC2REG_PRT_INPUT_STATUS) + */ +#define PCCTWO_PRT_IN_SR_BSY (1u<<0) /* State of printer's BSY Input */ +#define PCCTWO_PRT_IN_SR_PE (1u<<1) /* State of printer's PE Input */ +#define PCCTWO_PRT_IN_SR_SEL (1u<<2) /* State of printer's SELECT Input */ +#define PCCTWO_PRT_IN_SR_FLT (1u<<3) /* State of printer's FAULT Input */ +#define PCCTWO_PRT_IN_SR_ACK (1u<<4) /* State of printer's ACK Input */ +#define PCCTWO_PRT_IN_SR_PINT (1u<<7) /* Printer Interrupt Status */ + + +/* + * Printer Port Control Register (PCC2REG_PRT_CONTROL) + */ +#define PCCTWO_PRT_CTRL_MAN (1u<<0) /* Manual Strobe Control */ +#define PCCTWO_PRT_CTRL_FAST (1u<<1) /* Fast Auto Strobe */ +#define PCCTWO_PRT_CTRL_STB (1u<<2) /* Strobe Pin, in manual control mode */ +#define PCCTWO_PRT_CTRL_INP (1u<<3) /* Printer Input Prime */ +#define PCCTWO_PRT_CTRL_DOEN (1u<<4) /* Printer Data Output Enable */ + +#endif /* _MVME_PCCTWOREG_H */ diff --git a/sys/dev/mvme/pcctwovar.h b/sys/dev/mvme/pcctwovar.h new file mode 100644 index 00000000000..2b5c394cd40 --- /dev/null +++ b/sys/dev/mvme/pcctwovar.h @@ -0,0 +1,103 @@ +/* $NetBSD: pcctwovar.h,v 1.1 2002/02/12 20:38:50 scw Exp $ */ + +/*- + * Copyright (c) 2000, 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Steve C. Woodford + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#ifndef _MVME_PCCTWOVAR_H +#define _MVME_PCCTWOVAR_H + +/* + * Structure used to attach PCC devices. + */ +struct pcctwo_attach_args { + const char *pa_name; /* name of device */ + int pa_ipl; /* interrupt level */ + bus_dma_tag_t pa_dmat; /* DMA tag */ + bus_space_tag_t pa_bust; /* Bus tag */ + bus_addr_t pa_offset; /* Offset with 'Bus tag' bus space */ + + bus_addr_t _pa_base; +}; + +/* + * Structure used to describe a device for autoconfiguration purposes. + */ +struct pcctwo_device { + char *pcc_name; /* name of device (e.g. "clock") */ + bus_addr_t pcc_offset; /* offset from PCC2 base */ +}; + +/* + * Macroes to make life easy when converting vector offset to interrupt + * control register, and how to initialise the ICSR. + */ +#define VEC2ICSR(r,v) ((r) | (((v) | PCCTWO_ICR_IEN) << 8)) +#define VEC2ICSR_REG(x) ((x) & 0xff) +#define VEC2ICSR_INIT(x) ((x) >> 8) + +/* Shorthand for locators. */ +#include "locators.h" +#define pcctwocf_ipl cf_loc[PCCTWOCF_IPL] + +/* + * PCCChip2 driver's soft state structure + */ +struct pcctwo_softc { + struct device sc_dev; + bus_space_tag_t sc_bust; /* PCCChip2's register tag */ + bus_space_handle_t sc_bush; /* PCCChip2's register handle */ + bus_dma_tag_t sc_dmat; /* PCCChip2's dma tag (unused)*/ + int *sc_vec2icsr; /* Translate vector to ICSR */ + int sc_vecbase; + void *sc_isrcookie; + void (*sc_isrlink)(void *, int (*)(void *), void *, + int, int, struct evcnt *); + void (*sc_isrunlink)(void *, int); + struct evcnt * (*sc_isrevcnt)(void *, int); +#if defined(MVME162) || defined(MVME172) + struct evcnt sc_evcnt; +#endif +}; + +extern struct pcctwo_softc *sys_pcctwo; + +extern void pcctwo_init(struct pcctwo_softc *, struct pcctwo_device *, int); +extern struct evcnt *pcctwointr_evcnt(int); +extern void pcctwointr_establish(int, int (*)(void *), int, void *, + struct evcnt *); +extern void pcctwointr_disestablish(int); + +#endif /* _MVME_PCCTWOVAR_H */ diff --git a/sys/dev/mvme/vme_two.c b/sys/dev/mvme/vme_two.c new file mode 100644 index 00000000000..8eb7f191e0e --- /dev/null +++ b/sys/dev/mvme/vme_two.c @@ -0,0 +1,353 @@ +/* $NetBSD: vme_two.c,v 1.1 2002/02/12 20:38:50 scw Exp $ */ + +/*- + * Copyright (c) 1999, 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Steve C. Woodford. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* + * VME support specific to the VMEchip2 found on all high-end MVME boards + */ + +#include "vmetwo.h" + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/device.h> + +#include <machine/cpu.h> +#include <machine/bus.h> + +#include <dev/vme/vmereg.h> +#include <dev/vme/vmevar.h> + +#include <dev/mvme/mvmebus.h> +#include <dev/mvme/vme_tworeg.h> +#include <dev/mvme/vme_twovar.h> + +void vmetwo_master_range(struct vmetwo_softc *, int, struct mvmebus_range *); +void vmetwo_slave_range(struct vmetwo_softc *, int, vme_am_t, + struct mvmebus_range *); + +/* ARGSUSED */ +void +vmetwo_init(sc) + struct vmetwo_softc *sc; +{ + u_int32_t reg; + int i; + + /* Initialise stuff for the common mvmebus front-end */ + sc->sc_mvmebus.sc_chip = sc; + sc->sc_mvmebus.sc_nmasters = VME2_NMASTERS; + sc->sc_mvmebus.sc_masters = &sc->sc_master[0]; + sc->sc_mvmebus.sc_nslaves = VME2_NSLAVES; + sc->sc_mvmebus.sc_slaves = &sc->sc_slave[0]; + sc->sc_mvmebus.sc_intr_establish = vmetwo_intr_establish; + sc->sc_mvmebus.sc_intr_disestablish = vmetwo_intr_disestablish; + + /* Initialise interrupts */ + vmetwo_intr_init(sc); + + reg = vme2_lcsr_read(sc, VME2LCSR_BOARD_CONTROL); + printf(": Type 2 VMEchip, scon jumper %s\n", + (reg & VME2_BOARD_CONTROL_SCON) ? "enabled" : "disabled"); + + /* + * Figure out what bits of the VMEbus we can access. + * First record the `fixed' maps (if they're enabled) + */ + reg = vme2_lcsr_read(sc, VME2LCSR_IO_CONTROL); + if (reg & VME2_IO_CONTROL_I1EN) { + /* This range is fixed to A16, DATA */ + sc->sc_master[0].vr_am = VME_AM_A16 | MVMEBUS_AM_CAP_DATA; + + /* However, SUPER/USER is selectable... */ + if (reg & VME2_IO_CONTROL_I1SU) + sc->sc_master[0].vr_am |= MVMEBUS_AM_CAP_SUPER; + else + sc->sc_master[0].vr_am |= MVMEBUS_AM_CAP_USER; + + /* As is the datasize */ + sc->sc_master[0].vr_datasize = VME_D32 | VME_D16 | VME_D8; + if (reg & VME2_IO_CONTROL_I1D16) + sc->sc_master[0].vr_datasize &= ~VME_D32; + + sc->sc_master[0].vr_locstart = VME2_IO0_LOCAL_START; + sc->sc_master[0].vr_mask = VME2_IO0_MASK; + sc->sc_master[0].vr_vmestart = VME2_IO0_VME_START; + sc->sc_master[0].vr_vmeend = VME2_IO0_VME_END; + } else + sc->sc_master[0].vr_am = MVMEBUS_AM_DISABLED; + + if (reg & VME2_IO_CONTROL_I2EN) { + /* These two ranges are fixed to A24D16 and A32D16 */ + sc->sc_master[1].vr_am = VME_AM_A24; + sc->sc_master[1].vr_datasize = VME_D16 | VME_D8; + sc->sc_master[2].vr_am = VME_AM_A32; + sc->sc_master[2].vr_datasize = VME_D16 | VME_D8; + + /* However, SUPER/USER is selectable */ + if (reg & VME2_IO_CONTROL_I2SU) { + sc->sc_master[1].vr_am |= MVMEBUS_AM_CAP_SUPER; + sc->sc_master[2].vr_am |= MVMEBUS_AM_CAP_SUPER; + } else { + sc->sc_master[1].vr_am |= MVMEBUS_AM_CAP_USER; + sc->sc_master[2].vr_am |= MVMEBUS_AM_CAP_USER; + } + + /* As is PROGRAM/DATA */ + if (reg & VME2_IO_CONTROL_I2PD) { + sc->sc_master[1].vr_am |= MVMEBUS_AM_CAP_PROG; + sc->sc_master[2].vr_am |= MVMEBUS_AM_CAP_PROG; + } else { + sc->sc_master[1].vr_am |= MVMEBUS_AM_CAP_DATA; + sc->sc_master[2].vr_am |= MVMEBUS_AM_CAP_DATA; + } + + sc->sc_master[1].vr_locstart = VME2_IO1_LOCAL_START; + sc->sc_master[1].vr_mask = VME2_IO1_MASK; + sc->sc_master[1].vr_vmestart = VME2_IO1_VME_START; + sc->sc_master[1].vr_vmeend = VME2_IO1_VME_END; + + sc->sc_master[2].vr_locstart = VME2_IO2_LOCAL_START; + sc->sc_master[2].vr_mask = VME2_IO2_MASK; + sc->sc_master[2].vr_vmestart = VME2_IO2_VME_START; + sc->sc_master[2].vr_vmeend = VME2_IO2_VME_END; + } else { + sc->sc_master[1].vr_am = MVMEBUS_AM_DISABLED; + sc->sc_master[2].vr_am = MVMEBUS_AM_DISABLED; + } + + /* + * Now read the progammable maps + */ + for (i = 0; i < VME2_MASTER_WINDOWS; i++) + vmetwo_master_range(sc, i, + &(sc->sc_master[i + VME2_MASTER_PROG_START])); + + /* XXX: No A16 slave yet :XXX */ + sc->sc_slave[VME2_SLAVE_A16].vr_am = MVMEBUS_AM_DISABLED; + + for (i = 0; i < VME2_SLAVE_WINDOWS; i++) { + vmetwo_slave_range(sc, i, VME_AM_A32, + &sc->sc_slave[i + VME2_SLAVE_PROG_START]); + vmetwo_slave_range(sc, i, VME_AM_A24, + &sc->sc_slave[i + VME2_SLAVE_PROG_START + 2]); + } + + mvmebus_attach(&sc->sc_mvmebus); +} + +void +vmetwo_master_range(sc, range, vr) + struct vmetwo_softc *sc; + int range; + struct mvmebus_range *vr; +{ + u_int32_t start, end, attr; + u_int32_t reg; + + /* + * First, check if the range is actually enabled... + */ + reg = vme2_lcsr_read(sc, VME2LCSR_MASTER_ENABLE); + if ((reg & VME2_MASTER_ENABLE(range)) == 0) { + vr->vr_am = MVMEBUS_AM_DISABLED; + return; + } + + /* + * Fetch and record the range's attributes + */ + attr = vme2_lcsr_read(sc, VME2LCSR_MASTER_ATTR); + attr >>= VME2_MASTER_ATTR_AM_SHIFT(range); + + /* + * Fix up the datasizes available through this range + */ + vr->vr_datasize = VME_D32 | VME_D16 | VME_D8; + if (attr & VME2_MASTER_ATTR_D16) + vr->vr_datasize &= ~VME_D32; + attr &= VME2_MASTER_ATTR_AM_MASK; + + vr->vr_am = (attr & VME_AM_ADRSIZEMASK) | MVMEBUS_AM2CAP(attr); + switch (vr->vr_am & VME_AM_ADRSIZEMASK) { + case VME_AM_A32: + default: + vr->vr_mask = 0xffffffffu; + break; + + case VME_AM_A24: + vr->vr_mask = 0x00ffffffu; + break; + + case VME_AM_A16: + vr->vr_mask = 0x0000ffffu; + break; + } + + /* + * XXX + * It would be nice if users of the MI VMEbus code could pass down + * whether they can tolerate Write-Posting to their device(s). + * XXX + */ + + /* + * Fetch the local-bus start and end addresses for the range + */ + reg = vme2_lcsr_read(sc, VME2LCSR_MASTER_ADDRESS(range)); + start = (reg & VME2_MAST_ADDRESS_START_MASK); + start <<= VME2_MAST_ADDRESS_START_SHIFT; + end = (reg & VME2_MAST_ADDRESS_END_MASK); + end <<= VME2_MAST_ADDRESS_END_SHIFT; + + /* + * Local->VMEbus map '4' has optional translation bits, so + * the VMEbus start and end addresses may need to be adjusted. + */ + if (range == 3 && (reg = vme2_lcsr_read(sc, VME2LCSR_MAST4_TRANS))!=0) { + uint32_t addr, sel, len = end - start; + + vr->vr_locstart = start; + + reg = vme2_lcsr_read(sc, VME2LCSR_MAST4_TRANS); + reg &= VME2_MAST4_TRANS_SELECT_MASK; + sel = reg << VME2_MAST4_TRANS_SELECT_SHIFT; + + reg = vme2_lcsr_read(sc, VME2LCSR_MAST4_TRANS); + reg &= VME2_MAST4_TRANS_ADDRESS_MASK; + addr = reg << VME2_MAST4_TRANS_ADDRESS_SHIFT; + + start = (addr & sel) | (start & (~sel)); + end = start + len; + vr->vr_mask &= len - 1; + } else + vr->vr_locstart = 0; + + /* XXX Deal with overlap of onboard RAM address space */ + /* XXX Then again, 167-Bug warns about this at setup time ... */ + + /* + * Fixup the addresses this range corresponds to + */ + vr->vr_vmestart = start; + vr->vr_vmeend = end - 1; +} + +void +vmetwo_slave_range(sc, range, am, vr) + struct vmetwo_softc *sc; + int range; + vme_am_t am; + struct mvmebus_range *vr; +{ + u_int32_t reg; + + /* + * First, check if the range is actually enabled. + * Note that bit 1 of `range' is used to indicte if we're + * looking for an A24 range (set) or an A32 range (clear). + */ + reg = vme2_lcsr_read(sc, VME2LCSR_SLAVE_CTRL); + + if (am == VME_AM_A32 && (reg & VME2_SLAVE_AMSEL_A32(range))) { + vr->vr_am = VME_AM_A32; + vr->vr_mask = 0xffffffffu; + } else + if (am == VME_AM_A24 && (reg & VME2_SLAVE_AMSEL_A24(range))) { + vr->vr_am = VME_AM_A24; + vr->vr_mask = 0x00ffffffu; + } else { + /* The range is not enabled */ + vr->vr_am = MVMEBUS_AM_DISABLED; + return; + } + + if ((reg & VME2_SLAVE_AMSEL_DAT(range)) != 0) + vr->vr_am |= MVMEBUS_AM_CAP_DATA; + + if ((reg & VME2_SLAVE_AMSEL_PGM(range)) != 0) + vr->vr_am |= MVMEBUS_AM_CAP_PROG; + + if ((reg & VME2_SLAVE_AMSEL_USR(range)) != 0) + vr->vr_am |= MVMEBUS_AM_CAP_USER; + + if ((reg & VME2_SLAVE_AMSEL_SUP(range)) != 0) + vr->vr_am |= MVMEBUS_AM_CAP_SUPER; + + if ((reg & VME2_SLAVE_AMSEL_BLK(range)) != 0) + vr->vr_am |= MVMEBUS_AM_CAP_BLK; + + if ((reg & VME2_SLAVE_AMSEL_BLKD64(range)) != 0) + vr->vr_am |= MVMEBUS_AM_CAP_BLKD64; + + vr->vr_datasize = VME_D32 | VME_D16 | VME_D8; + + /* + * Record the VMEbus start and end addresses of the slave image + */ + reg = vme2_lcsr_read(sc, VME2LCSR_SLAVE_ADDRESS(range)); + vr->vr_vmestart = reg & VME2_SLAVE_ADDRESS_START_MASK; + vr->vr_vmestart <<= VME2_SLAVE_ADDRESS_START_SHIFT; + vr->vr_vmestart &= vr->vr_mask; + vr->vr_vmeend = reg & VME2_SLAVE_ADDRESS_END_MASK; + vr->vr_vmeend <<= VME2_SLAVE_ADDRESS_END_SHIFT; + vr->vr_vmeend &= vr->vr_mask; + vr->vr_vmeend |= 0xffffu; + + /* + * Now figure out the local-bus address + */ + reg = vme2_lcsr_read(sc, VME2LCSR_SLAVE_CTRL); + if ((reg & VME2_SLAVE_CTRL_ADDER(range)) != 0) { + reg = vme2_lcsr_read(sc, VME2LCSR_SLAVE_TRANS(range)); + reg &= VME2_SLAVE_TRANS_ADDRESS_MASK; + reg <<= VME2_SLAVE_TRANS_ADDRESS_SHIFT; + vr->vr_locstart = vr->vr_vmestart + reg; + } else { + u_int32_t sel, addr; + + reg = vme2_lcsr_read(sc, VME2LCSR_SLAVE_TRANS(range)); + sel = reg & VME2_SLAVE_TRANS_SELECT_MASK; + sel <<= VME2_SLAVE_TRANS_SELECT_SHIFT; + addr = reg & VME2_SLAVE_TRANS_ADDRESS_MASK; + addr <<= VME2_SLAVE_TRANS_ADDRESS_SHIFT; + + vr->vr_locstart = addr & sel; + vr->vr_locstart |= vr->vr_vmestart & (~sel); + } +} diff --git a/sys/dev/mvme/vme_two_isr.c b/sys/dev/mvme/vme_two_isr.c new file mode 100644 index 00000000000..d065bfabc9e --- /dev/null +++ b/sys/dev/mvme/vme_two_isr.c @@ -0,0 +1,429 @@ +/* $NetBSD: vme_two_isr.c,v 1.1 2002/02/12 20:38:51 scw Exp $ */ + +/*- + * Copyright (c) 2001, 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Steve C. Woodford. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* + * Split off from vme_two.c specifically to deal with hardware assisted + * soft interrupts when the user hasn't specified `vmetwo0' in the + * kernel config file (mvme1[67]2 only). + */ + +#include "vmetwo.h" + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/malloc.h> + +#include <machine/cpu.h> +#include <machine/bus.h> + +#include <dev/vme/vmereg.h> +#include <dev/vme/vmevar.h> + +#include <dev/mvme/mvmebus.h> +#include <dev/mvme/vme_tworeg.h> +#include <dev/mvme/vme_twovar.h> + +/* + * Non-zero if there is no VMEChip2 on this board. + */ +int vmetwo_not_present; + +/* + * Array of interrupt handlers registered with us for the non-VMEbus + * vectored interrupts. Eg. ABORT Switch, SYSFAIL etc. + * + * We can't just install a caller's handler directly, since these + * interrupts have to be manually cleared, so we have a trampoline + * which does the clearing automatically. + */ +static struct vme_two_handler { + int (*isr_hand) __P((void *)); + void *isr_arg; +} vme_two_handlers[(VME2_VECTOR_LOCAL_MAX - VME2_VECTOR_LOCAL_MIN) + 1]; + +#define VMETWO_HANDLERS_SZ (sizeof(vme_two_handlers) / \ + sizeof(struct vme_two_handler)) + +static int vmetwo_local_isr_trampoline(void *); +static void vmetwo_softintr_assert(void); + +static struct vmetwo_softc *vmetwo_sc; + +int +vmetwo_probe(bus_space_tag_t bt, bus_addr_t offset) +{ + bus_space_handle_t bh; + + bus_space_map(bt, offset + VME2REG_LCSR_OFFSET, VME2LCSR_SIZE, 0, &bh); + + if (bus_space_peek_4(bt, bh, VME2LCSR_MISC_STATUS, NULL) != 0) { +#if defined(MVME162) || defined(MVME172) +#if defined(MVME167) || defined(MVME177) + if (machineid == MVME_162 || machineid == MVME_172) +#endif + { + /* + * No VMEChip2 on mvme162/172 is not too big a + * deal; we can fall back on timer4 in the + * mcchip for h/w assisted soft interrupts... + */ + extern void pcctwosoftintrinit(void); + bus_space_unmap(bt, bh, VME2LCSR_SIZE); + vmetwo_not_present = 1; + pcctwosoftintrinit(); + return (0); + } +#endif +#if defined(MVME167) || defined(MVME177) || defined(MVME88K) + /* + * No VMEChip2 on mvme167/177, however, is a Big Deal. + * In fact, it means the hardware's shot since the + * VMEChip2 is not a `build-option' on those boards. + */ + panic("VMEChip2 not responding! Faulty board?"); + /* NOTREACHED */ +#endif +#if defined(MVMEPPC) + /* + * No VMEChip2 on mvmeppc is no big deal. + */ + bus_space_unmap(bt, bh, VME2LCSR_SIZE); + vmetwo_not_present = 1; + return (0); +#endif + } +#if NVMETWO == 0 + else { + /* + * The kernel config file has no `vmetwo0' device, but + * there is a VMEChip2 on the board. Fix up things + * just enough to hook VMEChip2 local interrupts. + */ + struct vmetwo_softc *sc; + + /* XXX Should check sc != NULL here... */ + MALLOC(sc, struct vmetwo_softc *, sizeof(*sc), M_DEVBUF, + M_NOWAIT); + + sc->sc_mvmebus.sc_bust = bt; + sc->sc_lcrh = bh; + vmetwo_intr_init(sc); + return 0; + } +#else + bus_space_unmap(bt, bh, VME2LCSR_SIZE); + return (1); +#endif +} + +void +vmetwo_intr_init(struct vmetwo_softc *sc) +{ + u_int32_t reg; + int i; + + vmetwo_sc = sc; + + /* Clear out the ISR handler array */ + for (i = 0; i < VMETWO_HANDLERS_SZ; i++) + vme_two_handlers[i].isr_hand = NULL; + + /* + * Initialize the chip. + * Firstly, disable all VMEChip2 Interrupts + */ + reg = vme2_lcsr_read(sc, VME2LCSR_MISC_STATUS) & ~VME2_MISC_STATUS_MIEN; + vme2_lcsr_write(sc, VME2LCSR_MISC_STATUS, reg); + vme2_lcsr_write(sc, VME2LCSR_LOCAL_INTERRUPT_ENABLE, 0); + vme2_lcsr_write(sc, VME2LCSR_LOCAL_INTERRUPT_CLEAR, + VME2_LOCAL_INTERRUPT_CLEAR_ALL); + + /* Zap all the IRQ level registers */ + for (i = 0; i < VME2_NUM_IL_REGS; i++) + vme2_lcsr_write(sc, VME2LCSR_INTERRUPT_LEVEL_BASE + (i * 4), 0); + + /* Disable the tick timers */ + reg = vme2_lcsr_read(sc, VME2LCSR_TIMER_CONTROL); + reg &= ~VME2_TIMER_CONTROL_EN(0); + reg &= ~VME2_TIMER_CONTROL_EN(1); + vme2_lcsr_write(sc, VME2LCSR_TIMER_CONTROL, reg); + + /* Set the VMEChip2's vector base register to the required value */ + reg = vme2_lcsr_read(sc, VME2LCSR_VECTOR_BASE); + reg &= ~VME2_VECTOR_BASE_MASK; + reg |= VME2_VECTOR_BASE_REG_VALUE; + vme2_lcsr_write(sc, VME2LCSR_VECTOR_BASE, reg); + + /* Set the Master Interrupt Enable bit now */ + reg = vme2_lcsr_read(sc, VME2LCSR_MISC_STATUS) | VME2_MISC_STATUS_MIEN; + vme2_lcsr_write(sc, VME2LCSR_MISC_STATUS, reg); + +#if defined(MVME167) || defined(MVME177) +#if defined(MVME162) || defined(MVME172) + if (machineid != MVME_162 && machineid != MVME_172) +#endif + { + /* + * Let the NMI handler deal with level 7 ABORT switch + * interrupts + */ + vmetwo_intr_establish(sc, 7, 7, VME2_VEC_ABORT, 1, + nmihand, NULL, NULL); + } +#endif + + /* Setup hardware assisted soft interrupts */ + vmetwo_intr_establish(sc, 1, 1, VME2_VEC_SOFT0, 1, + (int (*)(void *))softintr_dispatch, NULL, NULL); + _softintr_chipset_assert = vmetwo_softintr_assert; +} + +static int +vmetwo_local_isr_trampoline(arg) + void *arg; +{ + struct vme_two_handler *isr; + int vec; + + vec = (int) arg; /* 0x08 <= vec <= 0x1f */ + + /* Clear the interrupt source */ + vme2_lcsr_write(vmetwo_sc, VME2LCSR_LOCAL_INTERRUPT_CLEAR, + VME2_LOCAL_INTERRUPT(vec)); + + isr = &vme_two_handlers[vec - VME2_VECTOR_LOCAL_OFFSET]; + if (isr->isr_hand) + (void) (*isr->isr_hand) (isr->isr_arg); + else + printf("vmetwo: Spurious local interrupt, vector 0x%x\n", vec); + + return (1); +} + +void +vmetwo_local_intr_establish(pri, vec, hand, arg, evcnt) + int pri, vec; + int (*hand)(void *); + void *arg; + struct evcnt *evcnt; +{ + + vmetwo_intr_establish(vmetwo_sc, pri, pri, vec, 1, hand, arg, evcnt); +} + +/* ARGSUSED */ +void +vmetwo_intr_establish(csc, prior, lvl, vec, first, hand, arg, evcnt) + void *csc; + int prior, lvl, vec, first; + int (*hand)(void *); + void *arg; + struct evcnt *evcnt; +{ + struct vmetwo_softc *sc = csc; + u_int32_t reg; + int bitoff; + int iloffset, ilshift; + int s; + + s = splhigh(); + +#if NVMETWO > 0 + /* + * Sort out interrupts generated locally by the VMEChip2 from + * those generated by VMEbus devices... + */ + if (vec >= VME2_VECTOR_LOCAL_MIN && vec <= VME2_VECTOR_LOCAL_MAX) { +#endif + /* + * Local interrupts need to be bounced through some + * trampoline code which acknowledges/clears them. + */ + vme_two_handlers[vec - VME2_VECTOR_LOCAL_MIN].isr_hand = hand; + vme_two_handlers[vec - VME2_VECTOR_LOCAL_MIN].isr_arg = arg; + hand = vmetwo_local_isr_trampoline; + arg = (void *) (vec - VME2_VECTOR_BASE); + + /* + * Interrupt enable/clear bit offset is 0x08 - 0x1f + */ + bitoff = vec - VME2_VECTOR_BASE; +#if NVMETWO > 0 + first = 1; /* Force the interrupt to be enabled */ + } else { + /* + * Interrupts originating from the VMEbus are + * controlled by an offset of 0x00 - 0x07 + */ + bitoff = lvl - 1; + } +#endif + + /* Hook the interrupt */ + (*sc->sc_isrlink)(sc->sc_isrcookie, hand, arg, prior, vec, evcnt); + + /* + * Do we need to tell the VMEChip2 to let the interrupt through? + * (This is always true for locally-generated interrupts, but only + * needs doing once for each VMEbus interrupt level which is hooked) + */ +#if NVMETWO > 0 + if (first) { + if (evcnt) + evcnt_attach_dynamic(evcnt, EVCNT_TYPE_INTR, + (*sc->sc_isrevcnt)(sc->sc_isrcookie, prior), + sc->sc_mvmebus.sc_dev.dv_xname, + mvmebus_irq_name[lvl]); +#endif + iloffset = VME2_ILOFFSET_FROM_VECTOR(bitoff) + + VME2LCSR_INTERRUPT_LEVEL_BASE; + ilshift = VME2_ILSHIFT_FROM_VECTOR(bitoff); + + /* Program the specified interrupt to signal at 'prior' */ + reg = vme2_lcsr_read(sc, iloffset); + reg &= ~(VME2_INTERRUPT_LEVEL_MASK << ilshift); + reg |= (prior << ilshift); + vme2_lcsr_write(sc, iloffset, reg); + + /* Clear it */ + vme2_lcsr_write(sc, VME2LCSR_LOCAL_INTERRUPT_CLEAR, + VME2_LOCAL_INTERRUPT(bitoff)); + + /* Enable it. */ + reg = vme2_lcsr_read(sc, VME2LCSR_LOCAL_INTERRUPT_ENABLE); + reg |= VME2_LOCAL_INTERRUPT(bitoff); + vme2_lcsr_write(sc, VME2LCSR_LOCAL_INTERRUPT_ENABLE, reg); +#if NVMETWO > 0 + } +#ifdef DIAGNOSTIC + else { + /* Verify the interrupt priority is the same */ + iloffset = VME2_ILOFFSET_FROM_VECTOR(bitoff) + + VME2LCSR_INTERRUPT_LEVEL_BASE; + ilshift = VME2_ILSHIFT_FROM_VECTOR(bitoff); + + reg = vme2_lcsr_read(sc, iloffset); + reg &= (VME2_INTERRUPT_LEVEL_MASK << ilshift); + + if ((prior << ilshift) != reg) + panic("vmetwo_intr_establish: priority mismatch!"); + } +#endif +#endif + splx(s); +} + +void +vmetwo_intr_disestablish(csc, lvl, vec, last, evcnt) + void *csc; + int lvl, vec, last; + struct evcnt *evcnt; +{ + struct vmetwo_softc *sc = csc; + u_int32_t reg; + int iloffset, ilshift; + int bitoff; + int s; + + s = splhigh(); + +#if NVMETWO > 0 + /* + * Sort out interrupts generated locally by the VMEChip2 from + * those generated by VMEbus devices... + */ + if (vec >= VME2_VECTOR_LOCAL_MIN && vec <= VME2_VECTOR_LOCAL_MAX) { +#endif + /* + * Interrupt enable/clear bit offset is 0x08 - 0x1f + */ + bitoff = vec - VME2_VECTOR_BASE; + vme_two_handlers[vec - VME2_VECTOR_LOCAL_MIN].isr_hand = NULL; + last = 1; /* Force the interrupt to be cleared */ +#if NVMETWO > 0 + } else { + /* + * Interrupts originating from the VMEbus are + * controlled by an offset of 0x00 - 0x07 + */ + bitoff = lvl - 1; + } +#endif + + /* + * Do we need to tell the VMEChip2 to block the interrupt? + * (This is always true for locally-generated interrupts, but only + * needs doing once when the last VMEbus handler for any given level + * has been unhooked.) + */ + if (last) { + iloffset = VME2_ILOFFSET_FROM_VECTOR(bitoff) + + VME2LCSR_INTERRUPT_LEVEL_BASE; + ilshift = VME2_ILSHIFT_FROM_VECTOR(bitoff); + + /* Disable it. */ + reg = vme2_lcsr_read(sc, VME2LCSR_LOCAL_INTERRUPT_ENABLE); + reg &= ~VME2_LOCAL_INTERRUPT(bitoff); + vme2_lcsr_write(sc, VME2LCSR_LOCAL_INTERRUPT_ENABLE, reg); + + /* Set the interrupt's level to zero */ + reg = vme2_lcsr_read(sc, iloffset); + reg &= ~(VME2_INTERRUPT_LEVEL_MASK << ilshift); + vme2_lcsr_write(sc, iloffset, reg); + + /* Clear it */ + vme2_lcsr_write(sc, VME2LCSR_LOCAL_INTERRUPT_CLEAR, + VME2_LOCAL_INTERRUPT(vec)); + + if (evcnt) + evcnt_detach(evcnt); + } + /* Un-hook it */ + (*sc->sc_isrunlink)(sc->sc_isrcookie, vec); + + splx(s); +} + +static void +vmetwo_softintr_assert(void) +{ + + vme2_lcsr_write(vmetwo_sc, VME2LCSR_SOFTINT_SET, VME2_SOFTINT_SET(0)); +} diff --git a/sys/dev/mvme/vme_tworeg.h b/sys/dev/mvme/vme_tworeg.h new file mode 100644 index 00000000000..80d8609a28a --- /dev/null +++ b/sys/dev/mvme/vme_tworeg.h @@ -0,0 +1,526 @@ +/* $NetBSD: vme_tworeg.h,v 1.1 2002/02/12 20:38:51 scw Exp $ */ + +/*- + * Copyright (c) 1999, 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Steve C. Woodford. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#ifndef _MVME_VME_TWOREG_H +#define _MVME_VME_TWOREG_H + +/* + * Where the VMEchip2's registers live relative to the start + * of the VMEChip2's register space. + */ +#define VME2REG_LCSR_OFFSET 0x0000 +#define VME2REG_GCSR_OFFSET 0x0100 + + +/* + * Register map of the Type 2 VMEchip found on many MVME boards. + * Note: Only responds to D32 accesses. + */ + + /* + * Slave window configuration registers + */ +#define VME2_SLAVE_WINDOWS 2 +#define VME2LCSR_SLAVE_ADDRESS(x) (0x00 + ((x) * 4)) +#define VME2_SLAVE_ADDRESS_START_SHIFT 16 +#define VME2_SLAVE_ADDRESS_START_MASK (0x0000ffffu) +#define VME2_SLAVE_ADDRESS_END_SHIFT 0 +#define VME2_SLAVE_ADDRESS_END_MASK (0xffff0000u) + +#define VME2LCSR_SLAVE_TRANS(x) (0x08 + ((x) * 4)) +#define VME2_SLAVE_TRANS_SELECT_SHIFT 16 +#define VME2_SLAVE_TRANS_SELECT_MASK (0x0000ffffu) +#define VME2_SLAVE_TRANS_ADDRESS_SHIFT 0 +#define VME2_SLAVE_TRANS_ADDRESS_MASK (0xffff0000u) + +#define VME2LCSR_SLAVE_CTRL 0x10 +#define VME2_SLAVE_AMSEL_DAT(x) (1u << (0 + ((x) * 16))) +#define VME2_SLAVE_AMSEL_PGM(x) (1u << (1 + ((x) * 16))) +#define VME2_SLAVE_AMSEL_BLK(x) (1u << (2 + ((x) * 16))) +#define VME2_SLAVE_AMSEL_BLKD64(x) (1u << (3 + ((x) * 16))) +#define VME2_SLAVE_AMSEL_A24(x) (1u << (4 + ((x) * 16))) +#define VME2_SLAVE_AMSEL_A32(x) (1u << (5 + ((x) * 16))) +#define VME2_SLAVE_AMSEL_USR(x) (1u << (6 + ((x) * 16))) +#define VME2_SLAVE_AMSEL_SUP(x) (1u << (7 + ((x) * 16))) +#define VME2_SLAVE_CTRL_WP(x) (1u << (8 + ((x) * 16))) +#define VME2_SLAVE_CTRL_SNOOP_INHIBIT(x) (0u << (9 + ((x) * 16))) +#define VME2_SLAVE_CTRL_SNOOP_WRSINK(x) (1u << (9 + ((x) * 16))) +#define VME2_SLAVE_CTRL_SNOOP_WRINVAL(x) (2u << (9 + ((x) * 16))) +#define VME2_SLAVE_CTRL_ADDER(x) (1u << (11 + ((x) * 16))) + + /* + * Master window address control registers + */ +#define VME2_MASTER_WINDOWS 4 +#define VME2LCSR_MASTER_ADDRESS(x) (0x14 + ((x) * 4)) +#define VME2_MAST_ADDRESS_START_SHIFT 16 +#define VME2_MAST_ADDRESS_START_MASK (0x0000ffffu) +#define VME2_MAST_ADDRESS_END_SHIFT 0 +#define VME2_MAST_ADDRESS_END_MASK (0xffff0000u) + +#define VME2LCSR_MAST4_TRANS 0x24 +#define VME2_MAST4_TRANS_SELECT_SHIFT 16 +#define VME2_MAST4_TRANS_SELECT_MASK (0x0000ffffu) +#define VME2_MAST4_TRANS_ADDRESS_SHIFT 0 +#define VME2_MAST4_TRANS_ADDRESS_MASK (0xffff0000u) + + /* + * VMEbus master attribute control register + */ +#define VME2LCSR_MASTER_ATTR 0x28 +#define VME2_MASTER_ATTR_AM_SHIFT(x) ((x) * 8) +#define VME2_MASTER_ATTR_AM_MASK (0x0000003fu) +#define VME2_MASTER_ATTR_WP (1u << 6) +#define VME2_MASTER_ATTR_D16 (1u << 7) + + /* + * GCSR Group/Board addresses, and + * VMEbus Master Enable Control register, and + * Local to VMEbus I/O Control register, and + * ROM Control register (unused). + */ +#define VME2LCSR_GCSR_ADDRESS 0x2c +#define VME2_GCSR_ADDRESS_SHIFT 16 +#define VME2_GCSR_ADDRESS_MASK (0xfff00000u) + +#define VME2LCSR_MASTER_ENABLE 0x2c +#define VME2_MASTER_ENABLE_MASK (0x000f0000u) +#define VME2_MASTER_ENABLE(x) (1u << ((x) + 16)) + +#define VME2LCSR_IO_CONTROL 0x2c +#define VME2_IO_CONTROL_SHIFT 8 +#define VME2_IO_CONTROL_MASK (0x0000ff00u) +#define VME2_IO_CONTROL_I1SU (1u << 8) +#define VME2_IO_CONTROL_I1WP (1u << 9) +#define VME2_IO_CONTROL_I1D16 (1u << 10) +#define VME2_IO_CONTROL_I1EN (1u << 11) +#define VME2_IO_CONTROL_I2PD (1u << 12) +#define VME2_IO_CONTROL_I2SU (1u << 13) +#define VME2_IO_CONTROL_I2WP (1u << 14) +#define VME2_IO_CONTROL_I2EN (1u << 15) + + /* + * VMEChip2 PROM Decoder, SRAM and DMA Control register + */ +#define VME2LCSR_PROM_SRAM_DMA_CTRL 0x30 +#define VME2_PSD_SRAMS_MASK (0x00ff0000u) +#define VME2_PSD_SRAMS_CLKS6 (0u << 16) +#define VME2_PSD_SRAMS_CLKS5 (1u << 16) +#define VME2_PSD_SRAMS_CLKS4 (2u << 16) +#define VME2_PSD_SRAMS_CLKS3 (3u << 16) +#define VME2_PSD_TBLSC_INHIB (0u << 18) +#define VME2_PSD_TBLSC_WRSINK (1u << 18) +#define VME2_PSD_TBLSC_WRINV (2u << 18) +#define VME2_PSD_ROM0 (1u << 20) +#define VME2_PSD_WAITRMW (1u << 21) + + /* + * VMEbus requester control register + */ +#define VME2LCSR_VME_REQUESTER_CONTROL 0x30 +#define VME2_VMEREQ_CTRL_MASK (0x0000ff00u) +#define VME2_VMEREQ_CTRL_LVREQL_MASK (0x00000300u) +#define VME2_VMEREQ_CTRL_LVREQL(x) ((u_int)(x) << 8) +#define VME2_VMEREQ_CTRL_LVRWD (1u << 10) +#define VME2_VMEREQ_CTRL_LVFAIR (1u << 11) +#define VME2_VMEREQ_CTRL_DWB (1u << 13) +#define VME2_VMEREQ_CTRL_DHB (1u << 14) +#define VME2_VMEREQ_CTRL_ROBN (1u << 15) + + /* + * DMAC control register + */ +#define VME2LCSR_DMAC_CONTROL1 0x30 +#define VME2_DMAC_CTRL1_MASK (0x000000ffu) +#define VME2_DMAC_CTRL1_DREQL_MASK (0x00000003u) +#define VME2_DMAC_CTRL1_DREQL(x) ((u_int)(x) << 0) +#define VME2_DMAC_CTRL1_DRELM_MASK (0x0000000cu) +#define VME2_DMAC_CTRL1_DRELM(x) ((u_int)(x) << 2) +#define VME2_DMAC_CTRL1_DFAIR (1u << 4) +#define VME2_DMAC_CTRL1_DTBL (1u << 5) +#define VME2_DMAC_CTRL1_DEN (1u << 6) +#define VME2_DMAC_CTRL1_DHALT (1u << 7) + + /* + * DMA Control register #2 + */ +#define VME2LCSR_DMAC_CONTROL2 0x34 +#define VME2_DMAC_CTRL2_MASK (0x0000ffffu) +#define VME2_DMAC_CTRL2_SHIFT 0 +#define VME2_DMAC_CTRL2_AM_MASK (0x0000003fu) +#define VME2_DMAC_CTRL2_BLK_D32 (1u << 6) +#define VME2_DMAC_CTRL2_BLK_D64 (3u << 6) +#define VME2_DMAC_CTRL2_D16 (1u << 8) +#define VME2_DMAC_CTRL2_TVME (1u << 9) +#define VME2_DMAC_CTRL2_LINC (1u << 10) +#define VME2_DMAC_CTRL2_VINC (1u << 11) +#define VME2_DMAC_CTRL2_SNOOP_INHIB (0u << 13) +#define VME2_DMAC_CTRL2_SNOOP_WRSNK (1u << 13) +#define VME2_DMAC_CTRL2_SNOOP_WRINV (2u << 13) +#define VME2_DMAC_CTRL2_INTE (1u << 15) + + /* + * DMA Controller Local Bus and VMEbus Addresses, Byte + * Counter and Table Address Counter registers + */ +#define VME2LCSR_DMAC_LOCAL_ADDRESS 0x38 +#define VME2LCSR_DMAC_VME_ADDRESS 0x3c +#define VME2LCSR_DMAC_BYTE_COUNTER 0x40 +#define VME2LCSR_DMAC_TABLE_ADDRESS 0x44 + + /* + * VMEbus Interrupter Control register + */ +#define VME2LCSR_INTERRUPT_CONTROL 0x48 +#define VME2_INT_CTRL_MASK (0xff000000u) +#define VME2_INT_CTRL_SHIFT 24 +#define VME2_INT_CTRL_IRQL_MASK (0x07000000u) +#define VME2_INT_CTRL_IRQS (1u << 27) +#define VME2_INT_CTRL_IRQC (1u << 28) +#define VME2_INT_CTRL_IRQ1S_INT (0u << 29) +#define VME2_INT_CTRL_IRQ1S_TICK1 (1u << 29) +#define VME2_INT_CTRL_IRQ1S_TICK2 (3u << 29) + + /* + * VMEbus Interrupt Vector register + */ +#define VME2LCSR_INTERRUPT_VECTOR 0x48 +#define VME2_INTERRUPT_VECTOR_MASK (0x00ff0000u) +#define VME2_INTERRUPT_VECTOR_SHIFT 16 + + /* + * MPU Status register + */ +#define VME2LCSR_MPU_STATUS 0x48 +#define VME2_MPU_STATUS_MLOB (1u << 0) +#define VME2_MPU_STATUS_MLPE (1u << 1) +#define VME2_MPU_STATUS_MLBE (1u << 2) +#define VME2_MPU_STATUS_MCLR (1u << 3) + + /* + * DMA Interrupt Count register + */ +#define VME2LCSR_DMAC_INTERRUPT_CONTROL 0x48 +#define VME2_DMAC_INT_COUNT_MASK (0x0000f000u) +#define VME2_DMAC_INT_COUNT_SHIFT 12 + + /* + * DMA Controller Status register + */ +#define VME2LCSR_DMAC_STATUS 0x48 +#define VME2_DMAC_STATUS_DONE (1u << 0) +#define VME2_DMAC_STATUS_VME (1u << 1) +#define VME2_DMAC_STATUS_TBL (1u << 2) +#define VME2_DMAC_STATUS_DLTO (1u << 3) +#define VME2_DMAC_STATUS_DLOB (1u << 4) +#define VME2_DMAC_STATUS_DLPE (1u << 5) +#define VME2_DMAC_STATUS_DLBE (1u << 6) +#define VME2_DMAC_STATUS_MLTO (1u << 7) + + + /* + * VMEbus Arbiter Time-out register + */ +#define VME2LCSR_VME_ARB_TIMEOUT 0x4c +#define VME2_VME_ARB_TIMEOUT_ENAB (1u << 24) + + /* + * DMA Controller Timers and VMEbus Global Time-out Control registers + */ +#define VME2LCSR_DMAC_TIME_ONOFF 0x4c +#define VME2_DMAC_TIME_ON_MASK (0x001c0000u) +#define VME2_DMAC_TIME_ON_16US (0u << 18) +#define VME2_DMAC_TIME_ON_32US (1u << 18) +#define VME2_DMAC_TIME_ON_64US (2u << 18) +#define VME2_DMAC_TIME_ON_128US (3u << 18) +#define VME2_DMAC_TIME_ON_256US (4u << 18) +#define VME2_DMAC_TIME_ON_512US (5u << 18) +#define VME2_DMAC_TIME_ON_1024US (6u << 18) +#define VME2_DMAC_TIME_ON_DONE (7u << 18) +#define VME2_DMAC_TIME_OFF_MASK (0x00e00000u) +#define VME2_DMAC_TIME_OFF_0US (0u << 21) +#define VME2_DMAC_TIME_OFF_16US (1u << 21) +#define VME2_DMAC_TIME_OFF_32US (2u << 21) +#define VME2_DMAC_TIME_OFF_64US (3u << 21) +#define VME2_DMAC_TIME_OFF_128US (4u << 21) +#define VME2_DMAC_TIME_OFF_256US (5u << 21) +#define VME2_DMAC_TIME_OFF_512US (6u << 21) +#define VME2_DMAC_TIME_OFF_1024US (7u << 21) +#define VME2_VME_GLOBAL_TO_MASK (0x00030000u) +#define VME2_VME_GLOBAL_TO_8US (0u << 16) +#define VME2_VME_GLOBAL_TO_16US (1u << 16) +#define VME2_VME_GLOBAL_TO_256US (2u << 16) +#define VME2_VME_GLOBAL_TO_DISABLE (3u << 16) + + /* + * VME Access, Local Bus and Watchdog Time-out Control register + */ +#define VME2LCSR_VME_ACCESS_TIMEOUT 0x4c +#define VME2_VME_ACCESS_TIMEOUT_MASK (0x0000c000u) +#define VME2_VME_ACCESS_TIMEOUT_64US (0u << 14) +#define VME2_VME_ACCESS_TIMEOUT_1MS (1u << 14) +#define VME2_VME_ACCESS_TIMEOUT_32MS (2u << 14) +#define VME2_VME_ACCESS_TIMEOUT_DISABLE (3u << 14) + +#define VME2LCSR_LOCAL_BUS_TIMEOUT 0x4c +#define VME2_LOCAL_BUS_TIMEOUT_MASK (0x00003000u) +#define VME2_LOCAL_BUS_TIMEOUT_64US (0u << 12) +#define VME2_LOCAL_BUS_TIMEOUT_1MS (1u << 12) +#define VME2_LOCAL_BUS_TIMEOUT_32MS (2u << 12) +#define VME2_LOCAL_BUS_TIMEOUT_DISABLE (3u << 12) + +#define VME2LCSR_WATCHDOG_TIMEOUT 0x4c +#define VME2_WATCHDOG_TIMEOUT_MASK (0x00000f00u) +#define VME2_WATCHDOG_TIMEOUT_512US (0u << 8) +#define VME2_WATCHDOG_TIMEOUT_1MS (1u << 8) +#define VME2_WATCHDOG_TIMEOUT_2MS (2u << 8) +#define VME2_WATCHDOG_TIMEOUT_4MS (3u << 8) +#define VME2_WATCHDOG_TIMEOUT_8MS (4u << 8) +#define VME2_WATCHDOG_TIMEOUT_16MS (5u << 8) +#define VME2_WATCHDOG_TIMEOUT_32MS (6u << 8) +#define VME2_WATCHDOG_TIMEOUT_64MS (7u << 8) +#define VME2_WATCHDOG_TIMEOUT_128MS (8u << 8) +#define VME2_WATCHDOG_TIMEOUT_256MS (9u << 8) +#define VME2_WATCHDOG_TIMEOUT_512MS (10u << 8) +#define VME2_WATCHDOG_TIMEOUT_1S (11u << 8) +#define VME2_WATCHDOG_TIMEOUT_4S (12u << 8) +#define VME2_WATCHDOG_TIMEOUT_16S (13u << 8) +#define VME2_WATCHDOG_TIMEOUT_32S (14u << 8) +#define VME2_WATCHDOG_TIMEOUT_64S (15u << 8) + + /* + * Prescaler Control register + */ +#define VME2LCSR_PRESCALER_CONTROL 0x4c +#define VME2_PRESCALER_MASK (0x000000ffu) +#define VME2_PRESCALER_SHIFT 0 +#define VME2_PRESCALER_CTRL(c) (256 - (c)) + + /* + * Tick Timer registers + */ +#define VME2LCSR_TIMER_COMPARE(x) (0x50 + ((x) * 8)) +#define VME2LCSR_TIMER_COUNTER(x) (0x54 + ((x) * 8)) + + + /* + * Board Control register + */ +#define VME2LCSR_BOARD_CONTROL 0x60 +#define VME2_BOARD_CONTROL_RSWE (1u << 24) +#define VME2_BOARD_CONTROL_BDFLO (1u << 25) +#define VME2_BOARD_CONTROL_CPURS (1u << 26) +#define VME2_BOARD_CONTROL_PURS (1u << 27) +#define VME2_BOARD_CONTROL_BRFLI (1u << 28) +#define VME2_BOARD_CONTROL_SFFL (1u << 29) +#define VME2_BOARD_CONTROL_SCON (1u << 30) + + /* + * Watchdog Timer Control register + */ +#define VME2LCSR_WATCHDOG_TIMER_CONTROL 0x60 +#define VME2_WATCHDOG_TCONTROL_WDEN (1u << 16) +#define VME2_WATCHDOG_TCONTTRL_WDRSE (1u << 17) +#define VME2_WATCHDOG_TCONTTRL_WDSL (1u << 18) +#define VME2_WATCHDOG_TCONTTRL_WDBFE (1u << 19) +#define VME2_WATCHDOG_TCONTTRL_WDTO (1u << 20) +#define VME2_WATCHDOG_TCONTTRL_WDCC (1u << 21) +#define VME2_WATCHDOG_TCONTTRL_WDCS (1u << 22) +#define VME2_WATCHDOG_TCONTTRL_SRST (1u << 23) + + /* + * Tick Timer Control registers + */ +#define VME2LCSR_TIMER_CONTROL 0x60 +#define VME2_TIMER_CONTROL_EN(x) (1u << (0 + ((x) * 8))) +#define VME2_TIMER_CONTROL_COC(x) (1u << (1 + ((x) * 8))) +#define VME2_TIMER_CONTROL_COF(x) (1u << (2 + ((x) * 8))) +#define VME2_TIMER_CONTROL_OVF_SHIFT(x) (4 + ((x) * 8)) +#define VME2_TIMER_CONTROL_OVF_MASK(x) (0x000000f0u << (4 + ((x) * 8))) + + /* + * Prescaler Counter register + */ +#define VME2LCSR_PRESCALER_COUNTER 0x64 + + /* + * Local Bus Interrupter Status/Enable/Clear registers + */ +#define VME2LCSR_LOCAL_INTERRUPT_STATUS 0x68 +#define VME2LCSR_LOCAL_INTERRUPT_ENABLE 0x6c +#define VME2LCSR_LOCAL_INTERRUPT_CLEAR 0x74 +#define VME2_LOCAL_INTERRUPT(x) (1u << (x)) +#define VME2_LOCAL_INTERRUPT_VME(x) (1u << ((x) - 1)) +#define VME2_LOCAL_INTERRUPT_SWINT(x) (1u << ((x) + 8)) +#define VME2_LOCAL_INTERRUPT_LM(x) (1u << ((x) + 16)) +#define VME2_LOCAL_INTERRUPT_SIG(x) (1u << ((x) + 18)) +#define VME2_LOCAL_INTERRUPT_DMAC (1u << 22) +#define VME2_LOCAL_INTERRUPT_VIA (1u << 23) +#define VME2_LOCAL_INTERRUPT_TIC(x) (1u << ((x) + 24)) +#define VME2_LOCAL_INTERRUPT_VI1E (1u << 26) +#define VME2_LOCAL_INTERRUPT_PE (1u << 27) +#define VME2_LOCAL_INTERRUPT_MWP (1u << 28) +#define VME2_LOCAL_INTERRUPT_SYSF (1u << 29) +#define VME2_LOCAL_INTERRUPT_ABORT (1u << 30) +#define VME2_LOCAL_INTERRUPT_ACFAIL (1u << 31) +#define VME2_LOCAL_INTERRUPT_CLEAR_ALL (0xffffff00u) + + /* + * Software Interrupt Set register + */ +#define VME2LCSR_SOFTINT_SET 0x70 +#define VME2_SOFTINT_SET(x) (1u << ((x) + 8)) + + /* + * Interrupt Level registers + */ +#define VME2LCSR_INTERRUPT_LEVEL_BASE 0x78 +#define VME2_NUM_IL_REGS 4 +#define VME2_ILOFFSET_FROM_VECTOR(v) (((((VME2_NUM_IL_REGS*8)-1)-(v))/8)<<2) +#define VME2_ILSHIFT_FROM_VECTOR(v) (((v) & 7) * 4) +#define VME2_INTERRUPT_LEVEL_MASK (0x0fu) + + /* + * Vector Base register + */ +#define VME2LCSR_VECTOR_BASE 0x88 +#define VME2_VECTOR_BASE_MASK (0xff000000u) +#define VME2_VECTOR_BASE_REG_VALUE (0x76000000u) +#define VME2_VECTOR_BASE (0x60u) +#define VME2_VECTOR_LOCAL_OFFSET (0x08u) +#define VME2_VECTOR_LOCAL_MIN (VME2_VECTOR_BASE + 0x08u) +#define VME2_VECTOR_LOCAL_MAX (VME2_VECTOR_BASE + 0x1fu) +#define VME2_VEC_SOFT0 (VME2_VECTOR_BASE + 0x08u) +#define VME2_VEC_SOFT1 (VME2_VECTOR_BASE + 0x09u) +#define VME2_VEC_SOFT2 (VME2_VECTOR_BASE + 0x0au) +#define VME2_VEC_SOFT3 (VME2_VECTOR_BASE + 0x0bu) +#define VME2_VEC_SOFT4 (VME2_VECTOR_BASE + 0x0cu) +#define VME2_VEC_SOFT5 (VME2_VECTOR_BASE + 0x0du) +#define VME2_VEC_SOFT6 (VME2_VECTOR_BASE + 0x0eu) +#define VME2_VEC_SOFT7 (VME2_VECTOR_BASE + 0x0fu) +#define VME2_VEC_GCSRLM0 (VME2_VECTOR_BASE + 0x10u) +#define VME2_VEC_GCSRLM1 (VME2_VECTOR_BASE + 0x11u) +#define VME2_VEC_GCSRSIG0 (VME2_VECTOR_BASE + 0x12u) +#define VME2_VEC_GCSRSIG1 (VME2_VECTOR_BASE + 0x13u) +#define VME2_VEC_GCSRSIG2 (VME2_VECTOR_BASE + 0x14u) +#define VME2_VEC_GCSRSIG3 (VME2_VECTOR_BASE + 0x15u) +#define VME2_VEC_DMAC (VME2_VECTOR_BASE + 0x16u) +#define VME2_VEC_VIA (VME2_VECTOR_BASE + 0x17u) +#define VME2_VEC_TT1 (VME2_VECTOR_BASE + 0x18u) +#define VME2_VEC_TT2 (VME2_VECTOR_BASE + 0x19u) +#define VME2_VEC_IRQ1 (VME2_VECTOR_BASE + 0x1au) +#define VME2_VEC_PARITY_ERROR (VME2_VECTOR_BASE + 0x1bu) +#define VME2_VEC_MWP_ERROR (VME2_VECTOR_BASE + 0x1cu) +#define VME2_VEC_SYSFAIL (VME2_VECTOR_BASE + 0x1du) +#define VME2_VEC_ABORT (VME2_VECTOR_BASE + 0x1eu) +#define VME2_VEC_ACFAIL (VME2_VECTOR_BASE + 0x1fu) + + /* + * I/O Control register #1 + */ +#define VME2LCSR_GPIO_DIRECTION 0x88 +#define VME2_GPIO_DIRECTION_OUT(x) (1u << ((x) + 16)) + + /* + * Misc. Status register + */ +#define VME2LCSR_MISC_STATUS 0x88 +#define VME2_MISC_STATUS_ABRTL (1u << 20) +#define VME2_MISC_STATUS_ACFL (1u << 21) +#define VME2_MISC_STATUS_SYSFL (1u << 22) +#define VME2_MISC_STATUS_MIEN (1u << 23) + + /* + * GPIO Status register + */ +#define VME2LCSR_GPIO_STATUS 0x88 +#define VME2_GPIO_STATUS(x) (1u << ((x) + 8)) + + /* + * GPIO Control register #2 + */ +#define VME2LCSR_GPIO_CONTROL 0x88 +#define VME2_GPIO_CONTROL_SET(x) (1u << ((x) + 12)) + + /* + * General purpose input registers + */ +#define VME2LCSR_GP_INPUTS 0x88 +#define VME2_GP_INPUT(x) (1u << (x)) + + /* + * Miscellaneous Control register + */ +#define VME2LCSR_MISC_CONTROL 0x8c +#define VME2_MISC_CONTROL_DISBGN (1u << 0) +#define VME2_MISC_CONTROL_ENINT (1u << 1) +#define VME2_MISC_CONTROL_DISBSYT (1u << 2) +#define VME2_MISC_CONTROL_NOELBBSY (1u << 3) +#define VME2_MISC_CONTROL_DISMST (1u << 4) +#define VME2_MISC_CONTROL_DISSRAM (1u << 5) +#define VME2_MISC_CONTROL_REVEROM (1u << 6) +#define VME2_MISC_CONTROL_MPIRQEN (1u << 7) + +#define VME2LCSR_SIZE 0x90 + + +#define vme2_lcsr_read(s,r) \ + bus_space_read_4((s)->sc_mvmebus.sc_bust, (s)->sc_lcrh, (r)) +#define vme2_lcsr_write(s,r,v) \ + bus_space_write_4((s)->sc_mvmebus.sc_bust, (s)->sc_lcrh, (r), (v)) + + +/* + * Locations of the three fixed VMEbus I/O ranges + */ +#define VME2_IO0_LOCAL_START (0xffff0000u) +#define VME2_IO0_MASK (0x0000ffffu) +#define VME2_IO0_VME_START (0x00000000u) +#define VME2_IO0_VME_END (0x0000ffffu) + +#define VME2_IO1_LOCAL_START (0xf0000000u) +#define VME2_IO1_MASK (0x00ffffffu) +#define VME2_IO1_VME_START (0x00000000u) +#define VME2_IO1_VME_END (0x00ffffffu) + +#define VME2_IO2_LOCAL_START (0x00000000u) +#define VME2_IO2_MASK (0xffffffffu) +#define VME2_IO2_VME_START (0xf1000000u) /* Maybe starts@ 0x0? */ +#define VME2_IO2_VME_END (0xff7fffffu) + +#endif /* _MVME_VME_TWOREG_H */ diff --git a/sys/dev/mvme/vme_twovar.h b/sys/dev/mvme/vme_twovar.h new file mode 100644 index 00000000000..10b3e50cb1e --- /dev/null +++ b/sys/dev/mvme/vme_twovar.h @@ -0,0 +1,79 @@ +/* $NetBSD: vme_twovar.h,v 1.1 2002/02/12 20:38:52 scw Exp $ */ + +/*- + * Copyright (c) 1999, 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Steve C. Woodford. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#ifndef _MVME_VME_TWOVAR_H +#define _MVME_VME_TWOVAR_H + +#define VME2_MASTER_FIXED_A16 0 +#define VME2_MASTER_FIXED_A24 1 +#define VME2_MASTER_FIXED_A32 2 +#define VME2_MASTER_PROG_START 3 +#define VME2_SLAVE_OFFBOARD_RAM 0 +#define VME2_SLAVE_PROG_START 1 +#define VME2_SLAVE_A16 (VME2_SLAVE_PROG_START+(VME2_SLAVE_WINDOWS*2)) + +#define VME2_NMASTERS (VME2_MASTER_PROG_START + VME2_MASTER_WINDOWS) +#define VME2_NSLAVES (VME2_SLAVE_A16 + 1) + +struct vmetwo_softc { + struct mvmebus_softc sc_mvmebus; + bus_space_handle_t sc_lcrh; + bus_space_handle_t sc_gcrh; + void *sc_isrcookie; + void (*sc_isrlink)(void *, int (*)(void *), + void *, int, int, struct evcnt *); + void (*sc_isrunlink)(void *, int); + struct evcnt * (*sc_isrevcnt)(void *, int); +#if NVMETWO > 0 + struct mvmebus_range sc_master[VME2_NMASTERS]; + struct mvmebus_range sc_slave[VME2_NSLAVES]; +#endif +}; + +extern int vmetwo_not_present; + +void vmetwo_init(struct vmetwo_softc *); +int vmetwo_probe(bus_space_tag_t, bus_addr_t); +void vmetwo_intr_init(struct vmetwo_softc *); +void vmetwo_intr_establish(void *, int, int, int, int, + int (*)(void *), void *, struct evcnt *); +void vmetwo_intr_disestablish(void *, int, int, int, struct evcnt *); +void vmetwo_local_intr_establish(int, int, + int (*)(void *), void *, struct evcnt *); + +#endif /* _MVME_VME_TWOVAR_H */ |
