summaryrefslogtreecommitdiff
path: root/sys/arch/mvme68k/dev/vme.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arch/mvme68k/dev/vme.c')
-rw-r--r--sys/arch/mvme68k/dev/vme.c557
1 files changed, 557 insertions, 0 deletions
diff --git a/sys/arch/mvme68k/dev/vme.c b/sys/arch/mvme68k/dev/vme.c
new file mode 100644
index 00000000000..ddd4099dd63
--- /dev/null
+++ b/sys/arch/mvme68k/dev/vme.c
@@ -0,0 +1,557 @@
+/* $NetBSD$ */
+
+/*
+ * Copyright (c) 1995 Theo de Raadt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Theo de Raadt
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/ioctl.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/tty.h>
+#include <sys/uio.h>
+#include <sys/callout.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+#include <sys/fcntl.h>
+#include <sys/device.h>
+#include <machine/autoconf.h>
+#include <machine/cpu.h>
+#include <mvme68k/dev/vme.h>
+
+#include "pcc.h"
+#include "mc.h"
+#include "pcctwo.h"
+
+#if NPCC > 0
+#include <mvme68k/dev/pccreg.h>
+#endif
+#if NMC > 0
+#include <mvme68k/dev/mcreg.h>
+#endif
+#if NPCCTWO > 0
+#include <mvme68k/dev/pcctworeg.h>
+#endif
+
+int vmematch __P((struct device *, void *, void *));
+void vmeattach __P((struct device *, struct device *, void *));
+
+int vme1chip_init __P((struct vmesoftc *sc));
+int vme2chip_init __P((struct vmesoftc *sc));
+u_long vme2chip_map __P((u_long base, int len, int dwidth));
+int vme2abort __P((struct frame *frame));
+
+static int vmebustype;
+
+struct cfdriver vmecd = {
+ NULL, "vme", vmematch, vmeattach,
+ DV_DULL, sizeof(struct vmesoftc), 0
+};
+
+int
+vmematch(parent, cf, args)
+ struct device *parent;
+ void *cf;
+ void *args;
+{
+ struct confargs *ca = args;
+
+#if NMC > 0
+ if (ca->ca_bustype == BUS_MC) {
+ struct mcreg *mc = (struct mcreg *)ca->ca_master;
+
+ if (mc->mc_ver & MC_VER_NOVME)
+ return (0);
+ }
+#endif
+ return (1);
+}
+
+/*
+ * Returns a physical address mapping for a VME address & length.
+ * Note: on some hardware it is not possible to create certain
+ * mappings, ie. the MVME147 cannot do 32 bit accesses to VME bus
+ * addresses from 0 to physmem.
+ */
+caddr_t
+vmepmap(sc, vmeaddr, len, bustype)
+ struct vmesoftc *sc;
+ caddr_t vmeaddr;
+ int len;
+ int bustype;
+{
+ u_long base = (u_long)vmeaddr;
+
+ len = roundup(len, NBPG);
+ switch (vmebustype) {
+#if NPCC > 0
+ case BUS_PCC:
+ switch (bustype) {
+ case BUS_VMES:
+ if (base > VME1_A16BASE &&
+ (base+len - VME1_A16BASE) < VME1_A16D16LEN)
+ base = base - VME1_A16BASE + VME1_A16D16BASE;
+ else if (base+len < VME1_A32D16LEN)
+ base = base + VME1_A32D16BASE;
+ else {
+ printf("%s: cannot map pa %x len %x\n",
+ sc->sc_dev.dv_xname, base, len);
+ return (NULL);
+ }
+ break;
+ case BUS_VMEL:
+ if (base >= physmem && (base+len) < VME1_A32D32LEN)
+ base = base + VME1_A32D32BASE;
+ else if (base+len < VME1_A32D16LEN) /* HACK! */
+ base = base + VME1_A32D16BASE;
+ else {
+ printf("%s: cannot map pa %x len %x\n",
+ sc->sc_dev.dv_xname, base, len);
+ return (NULL);
+ }
+ break;
+ }
+ break;
+#endif
+#if NMC > 0 || NPCCTWO > 0
+ case BUS_MC:
+ case BUS_PCCTWO:
+ switch (bustype) {
+ case BUS_VMES:
+ if (base > VME2_A16BASE &&
+ (base+len-VME2_A16BASE) < VME2_A16D16LEN)
+ base = base - VME2_A16BASE + VME2_A16D16BASE;
+ else if (base > VME2_A24BASE &&
+ (base+len-VME2_A24BASE) < VME2_A24D16LEN)
+ base = base - VME2_A24BASE + VME2_A24D16BASE;
+ else if ((base+len) < VME2_A32D16LEN)
+ base = base + VME2_A32D16BASE;
+ else {
+ base = vme2chip_map(base, len, 16);
+ if (base == NULL)
+ return (NULL);
+ }
+ break;
+ case BUS_VMEL:
+#if 0
+ if (base > VME2_A16BASE &&
+ (base+len-VME2_A16BASE) < VME2_A16D32LEN)
+ base = base - VME2_A16BASE + VME2_A16D32BASE;
+#endif
+ base = vme2chip_map(base, len, 32);
+ if (base == NULL)
+ return (NULL);
+ break;
+ }
+ break;
+#endif
+ }
+ return ((caddr_t)base);
+}
+
+/* if successful, returns the va of a vme bus mapping */
+caddr_t
+vmemap(sc, vmeaddr, len, bustype)
+ struct vmesoftc *sc;
+ caddr_t vmeaddr;
+ int len;
+ int bustype;
+{
+ caddr_t pa, va;
+
+ pa = vmepmap(sc, pa, len, bustype);
+ if (pa == NULL)
+ return (NULL);
+ va = mapiodev(pa, len);
+ return (va);
+}
+
+void
+vmeunmap(va, len)
+ caddr_t va;
+ int len;
+{
+ unmapiodev(va, len);
+}
+
+int
+vmerw(sc, uio, flags, bus)
+ struct vmesoftc *sc;
+ struct uio *uio;
+ int flags;
+ int bus;
+{
+ register vm_offset_t o, v;
+ register int c;
+ register struct iovec *iov;
+ caddr_t vme;
+ int error = 0;
+
+ while (uio->uio_resid > 0 && error == 0) {
+ iov = uio->uio_iov;
+ if (iov->iov_len == 0) {
+ uio->uio_iov++;
+ uio->uio_iovcnt--;
+ if (uio->uio_iovcnt < 0)
+ panic("vmerw");
+ continue;
+ }
+
+ v = uio->uio_offset;
+ c = min(iov->iov_len, MAXPHYS);
+ if ((v & PGOFSET) + c > NBPG) /* max NBPG at a time */
+ c = NBPG - (v & PGOFSET);
+ if (c == 0)
+ return (0);
+ vme = vmemap(sc, (caddr_t)(v & ~PGOFSET),
+ NBPG, BUS_VMES);
+ if (vme == NULL) {
+ error = EFAULT; /* XXX? */
+ continue;
+ }
+ error = uiomove((caddr_t)vme + (v & PGOFSET), c, uio);
+ vmeunmap(vme, NBPG);
+ }
+ return (error);
+}
+
+int
+vmeprint(args, bus)
+ void *args;
+ char *bus;
+{
+ struct confargs *ca = args;
+
+ printf(" offset 0x%x", ca->ca_offset);
+ if (ca->ca_vec > 0)
+ printf(" vec %d", ca->ca_vec);
+ if (ca->ca_ipl > 0)
+ printf(" ipl %d", ca->ca_ipl);
+ return (UNCONF);
+}
+
+int
+vmescan(parent, child, args, bustype)
+ struct device *parent;
+ void *child, *args;
+ int bustype;
+{
+ struct cfdata *cf = child;
+ struct vmesoftc *sc = (struct vmesoftc *)parent;
+ struct confargs *ca = args;
+ struct confargs oca;
+
+ if (parent->dv_cfdata->cf_driver->cd_indirect) {
+ printf(" indirect devices not supported\n");
+ return 0;
+ }
+
+ bzero(&oca, sizeof oca);
+ oca.ca_bustype = bustype;
+ oca.ca_paddr = (void *)cf->cf_loc[0];
+ oca.ca_len = cf->cf_loc[1];
+ oca.ca_vec = cf->cf_loc[2];
+ oca.ca_ipl = cf->cf_loc[3];
+ if (oca.ca_ipl > 0 && oca.ca_vec == -1)
+ oca.ca_vec = intr_freevec();
+
+ oca.ca_offset = (int)oca.ca_paddr;
+ oca.ca_vaddr = (void *)vmemap(sc, oca.ca_paddr, oca.ca_len,
+ oca.ca_bustype);
+ if (!oca.ca_vaddr)
+ oca.ca_vaddr = (void *)-1;
+ oca.ca_master = (void *)sc;
+ oca.ca_name = cf->cf_driver->cd_name;
+ if ((*cf->cf_driver->cd_match)(parent, cf, &oca) == 0) {
+ if (oca.ca_vaddr != (void *)-1)
+ vmeunmap(oca.ca_vaddr, oca.ca_len);
+ return (0);
+ }
+ config_attach(parent, cf, &oca, vmeprint);
+ return (1);
+}
+
+void
+vmeattach(parent, self, args)
+ struct device *parent, *self;
+ void *args;
+{
+ struct vmesoftc *sc = (struct vmesoftc *)self;
+ struct confargs *ca = args;
+ struct vme1reg *vme1;
+ struct vme2reg *vme2;
+ int scon;
+
+ sc->sc_vaddr = ca->ca_vaddr;
+
+ vmebustype = ca->ca_bustype;
+ switch (ca->ca_bustype) {
+#if NPCC > 0
+ case BUS_PCC:
+ vme1 = (struct vme1reg *)sc->sc_vaddr;
+ scon = (vme1->vme1_scon & VME1_SCON_SWITCH);
+ printf(": %sscon\n", scon ? "" : "not ");
+ vme1chip_init(sc);
+ break;
+#endif
+#if NMC > 0 || NPCCTWO > 0
+ case BUS_MC:
+ case BUS_PCCTWO:
+ vme2 = (struct vme2reg *)sc->sc_vaddr;
+ scon = (vme2->vme2_tctl & VME2_TCTL_SCON);
+ printf(": %sscon\n", scon ? "" : "not ");
+ vme2chip_init(sc);
+ break;
+#endif
+ }
+
+ while (config_found(self, NULL, NULL))
+ ;
+}
+
+/*
+ * On the VMEbus, only one cpu may be configured to respond to any
+ * particular vme ipl. Therefore, it wouldn't make sense to globally
+ * enable all the interrupts all the time -- it would not be possible
+ * to put two cpu's and one vme card into a single cage. Rather, we
+ * enable each vme interrupt only when we are attaching a device that
+ * uses it. This makes it easier (though not trivial) to put two cpu
+ * cards in one VME cage, and both can have some limited access to vme
+ * interrupts (just can't share the same irq).
+ * Obviously no check is made to see if another cpu is using that
+ * interrupt. If you share you will lose.
+ */
+int
+vmeintr_establish(vec, ih)
+ int vec;
+ struct intrhand *ih;
+{
+ struct vmesoftc *sc = (struct vmesoftc *) vmecd.cd_devs[0];
+#if NPCC > 0
+ struct vme1reg *vme1;
+#endif
+#if NMC > 0 || NPCCTWO > 0
+ struct vme2reg *vme2;
+#endif
+ int x;
+
+ x = (intr_establish(vec, ih));
+
+ switch (vmebustype) {
+#if NPCC > 0
+ case BUS_PCC:
+ vme1 = (struct vme1reg *)sc->sc_vaddr;
+ vme1->vme1_irqen = vme1->vme1_irqen |
+ VME1_IRQ_VME(ih->ih_ipl);
+ break;
+#endif
+#if NMC > 0 || NPCCTWO > 0
+ case BUS_MC:
+ case BUS_PCCTWO:
+ vme2 = (struct vme2reg *)sc->sc_vaddr;
+ vme2->vme2_irqen = vme2->vme2_irqen |
+ VME2_IRQ_VME(ih->ih_ipl);
+ break;
+#endif
+ }
+ return (x);
+}
+
+#if defined(MVME147)
+int
+vme1chip_init(sc)
+ struct vmesoftc *sc;
+{
+ struct vme1reg *vme1 = (struct vme1reg *)sc->sc_vaddr;
+
+ vme1->vme1_scon &= ~VME1_SCON_SYSFAIL; /* XXX doesn't work */
+}
+#endif
+
+#if defined(MVME162) || defined(MVME167) || defined(MVME177)
+
+/*
+ * make local addresses 1G-2G correspond to VME addresses 3G-4G,
+ * as D32
+ */
+#define VME2_D32STARTPHYS (1*1024*1024*1024UL)
+#define VME2_D32ENDPHYS (2*1024*1024*1024UL)
+#define VME2_D32STARTVME (3*1024*1024*1024UL)
+#define VME2_D32BITSVME (3*1024*1024*1024UL)
+
+/*
+ * make local addresses 3G-3.75G correspond to VME addresses 3G-3.75G,
+ * as D16
+ */
+#define VME2_D16STARTPHYS (3*1024*1024*1024UL)
+#define VME2_D16ENDPHYS (3*1024*1024*1024UL + 768*1024*1024UL)
+
+/*
+ * XXX what AM bits should be used for the D32/D16 mappings?
+ */
+int
+vme2chip_init(sc)
+ struct vmesoftc *sc;
+{
+ struct vme2reg *vme2 = (struct vme2reg *)sc->sc_vaddr;
+ u_long ctl;
+
+ /* turn off SYSFAIL LED */
+ vme2->vme2_tctl &= ~VME2_TCTL_SYSFAIL;
+
+ ctl = vme2->vme2_masterctl;
+
+#if 0
+ /* unused decoders 1 & 2 */
+ printf("%s: phys 0x%08x-0x%08x to VMExxx 0x%08x-0x%08x\n",
+ sc->sc_dev.dv_xname,
+ vme2->vme2_master1 << 16, vme2->vme2_master1 & 0xffff0000,
+ vme2->vme2_master1 << 16, vme2->vme2_master1 & 0xffff0000);
+ printf("%s: phys 0x%08x-0x%08x to VMExxx 0x%08x-0x%08x\n",
+ sc->sc_dev.dv_xname,
+ vme2->vme2_master2 << 16, vme2->vme2_master2 & 0xffff0000,
+ vme2->vme2_master2 << 16, vme2->vme2_master2 & 0xffff0000);
+#endif
+
+ /* setup a D16 space */
+ vme2->vme2_master3 = ((VME2_D16ENDPHYS-1) & 0xffff0000) |
+ (VME2_D16STARTPHYS >> 16);
+ ctl &= ~(VME2_MASTERCTL_ALL << VME2_MASTERCTL_3SHIFT);
+ ctl |= (VME2_MASTERCTL_AM32SP | VME2_MASTERCTL_D16) <<
+ VME2_MASTERCTL_3SHIFT;
+#if 0
+ printf("%s: phys 0x%08x-0x%08x to VMED16 0x%08x-0x%08x\n",
+ sc->sc_dev.dv_xname,
+ VME2_D16STARTPHYS, VME2_D16ENDPHYS-1,
+ VME2_D16STARTPHYS, VME2_D16ENDPHYS-1);
+#endif
+
+ /* setup a D32 space */
+ vme2->vme2_master4 = ((VME2_D32ENDPHYS-1) & 0xffff0000) |
+ (VME2_D32STARTPHYS >> 16);
+ vme2->vme2_master4mod = (VME2_D32STARTVME & 0xffff0000) |
+ (VME2_D32BITSVME >> 16);
+ ctl &= ~(VME2_MASTERCTL_ALL << VME2_MASTERCTL_4SHIFT);
+ ctl |= (VME2_MASTERCTL_AM32SP) <<
+ VME2_MASTERCTL_4SHIFT;
+#if 0
+ printf("%s: phys 0x%08x-0x%08x to VMED32 0x%08x-0x%08x\n",
+ sc->sc_dev.dv_xname,
+ VME2_D32STARTPHYS, VME2_D32ENDPHYS-1,
+ VME2_D32STARTVME, VME2_D32STARTVME | ~VME2_D32BITSVME);
+#endif
+
+ vme2->vme2_masterctl = ctl;
+
+ ctl = vme2->vme2_gcsrctl;
+
+ /* enable A16 short IO map decoder (0xffffxxxx) */
+ ctl &= ~(VME2_GCSRCTL_I1EN | VME2_GCSRCTL_I1D16 | VME2_GCSRCTL_I1WP |
+ VME2_GCSRCTL_I1SU);
+ ctl |= VME2_GCSRCTL_I1EN | VME2_GCSRCTL_I1D16 | VME2_GCSRCTL_I1SU;
+
+ /* enable A24D16 (0xf0xxxxxx) and A32D16 (0xf[1-e]xxxxxx) decoders */
+ ctl &= ~(VME2_GCSRCTL_I2EN | VME2_GCSRCTL_I2WP | VME2_GCSRCTL_I2SU |
+ VME2_GCSRCTL_I2PD);
+ ctl |= VME2_GCSRCTL_I2EN | VME2_GCSRCTL_I2SU | VME2_GCSRCTL_I2PD;
+
+ /* map decoders 3 & 4 which were just configured */
+ ctl &= ~(VME2_GCSRCTL_MDEN4 | VME2_GCSRCTL_MDEN3 | VME2_GCSRCTL_MDEN1 |
+ VME2_GCSRCTL_MDEN2);
+ ctl |= VME2_GCSRCTL_MDEN4 | VME2_GCSRCTL_MDEN3;
+
+ vme2->vme2_gcsrctl = ctl;
+
+ /*
+ * Map the VME irq levels to the cpu levels 1:1.
+ * This is rather inflexible, but much easier.
+ */
+ vme2->vme2_irql4 = (7 << VME2_IRQL4_VME7SHIFT) |
+ (6 << VME2_IRQL4_VME6SHIFT) | (5 << VME2_IRQL4_VME5SHIFT) |
+ (4 << VME2_IRQL4_VME4SHIFT) | (3 << VME2_IRQL4_VME3SHIFT) |
+ (2 << VME2_IRQL4_VME2SHIFT) | (1 << VME2_IRQL4_VME1SHIFT);
+
+#if NPCCTWO > 0
+ if (vmebustype == BUS_PCCTWO) {
+ sc->sc_abih.ih_fn = vme2abort;
+ sc->sc_abih.ih_arg = 0;
+ sc->sc_abih.ih_ipl = 7;
+ sc->sc_abih.ih_wantframe = 1;
+
+ intr_establish(110, &sc->sc_abih); /* XXX 110 */
+ vme2->vme2_irqen |= VME2_IRQ_AB;
+ }
+#endif
+}
+
+/*
+ * A32 accesses on the MVME1[67]x require setting up mappings in
+ * the VME2 chip.
+ * XXX VME address must be between 2G and 4G
+ * XXX We only support D32 at the moment..
+ */
+u_long
+vme2chip_map(base, len, dwidth)
+ u_long base;
+ int len, dwidth;
+{
+ switch (dwidth) {
+ case 16:
+ if (base < VME2_D16STARTPHYS ||
+ base + (u_long)len > VME2_D16ENDPHYS)
+ return (NULL);
+ return (base);
+ case 32:
+ if (base < VME2_D32STARTVME)
+ return (NULL);
+ return (base - VME2_D32STARTVME + VME2_D32STARTPHYS);
+ }
+}
+
+#if NPCCTWO > 0
+int
+vme2abort(frame)
+ struct frame *frame;
+{
+ struct vmesoftc *sc = (struct vmesoftc *)vmecd.cd_devs[0];
+ struct vme2reg *vme2 = (struct vme2reg *)sc->sc_vaddr;
+
+ if (vme2->vme2_irqstat & VME2_IRQ_AB == 0) {
+ printf("%s: vme2chip irq not set\n", sc->sc_dev.dv_xname);
+ return (0);
+ }
+ vme2->vme2_irqclr = VME2_IRQ_AB;
+ nmihand(frame);
+ return (1);
+}
+#endif
+
+#endif