diff options
Diffstat (limited to 'sys/arch/mvme68k/dev/wdsc.c')
-rw-r--r-- | sys/arch/mvme68k/dev/wdsc.c | 499 |
1 files changed, 328 insertions, 171 deletions
diff --git a/sys/arch/mvme68k/dev/wdsc.c b/sys/arch/mvme68k/dev/wdsc.c index 229470ca055..1fa476ba926 100644 --- a/sys/arch/mvme68k/dev/wdsc.c +++ b/sys/arch/mvme68k/dev/wdsc.c @@ -1,6 +1,56 @@ -/* $OpenBSD: wdsc.c,v 1.17 2010/06/28 18:31:01 krw Exp $ */ +/* $OpenBSD: wdsc.c,v 1.18 2013/05/19 20:32:47 miod Exp $ */ /* + * Copyright (c) 2013 Miodrag Vallat. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/* + * Copyright (c) 2001 Wayne Knowles + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Wayne Knowles + * + * 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) 1996 Steve Woodford * Copyright (c) 1982, 1990 The Regents of the University of California. * All rights reserved. @@ -31,251 +81,302 @@ * * @(#)wdsc.c */ + #include <sys/param.h> #include <sys/systm.h> #include <sys/kernel.h> #include <sys/device.h> + +#include <uvm/uvm_extern.h> + #include <scsi/scsi_all.h> #include <scsi/scsiconf.h> -#include <mvme68k/dev/dmavar.h> -#include <mvme68k/dev/sbicreg.h> -#include <mvme68k/dev/sbicvar.h> -#include <mvme68k/dev/wdscreg.h> + #include <machine/autoconf.h> -#include <mvme68k/dev/pccreg.h> -void wdscattach(struct device *, struct device *, void *); -int wdscmatch(struct device *, struct cfdata *, void *); +#include <mvme68k/dev/pccreg.h> +#include <mvme68k/dev/wdscreg.h> -void wdsc_enintr(struct sbic_softc *); -int wdsc_dmago(struct sbic_softc *, char *, int, int); -int wdsc_dmanext(struct sbic_softc *); -void wdsc_dmastop(struct sbic_softc *); -int wdsc_dmaintr(void *); -int wdsc_scsiintr(void *); +#include <dev/ic/wd33c93reg.h> +#include <dev/ic/wd33c93var.h> -extern void sbicinit(struct sbic_softc *); -extern int sbicintr(struct sbic_softc *); +struct dma_table_entry { + uint32_t dc_paddr; + uint32_t dc_cnt; +}; -struct scsi_adapter wdsc_scsiswitch = { - sbic_scsicmd, - scsi_minphys, - 0, /* no lun support */ - 0, /* no lun support */ +struct wdsc_softc { + struct wd33c93_softc sc_wd33c93; + bus_dma_tag_t sc_dmat; + bus_dmamap_t sc_dmamap; + bus_dmamap_t sc_tablemap; + bus_dma_segment_t sc_tableseg; + vaddr_t sc_tableva; + struct intrhand sc_dmaih; + struct intrhand sc_wdscih; + int sc_ipl; + int sc_flags; +#define WDSC_DMA_ACTIVE 0x01 +#define WDSC_DMA_MAPLOADED 0x02 + u_short sc_dmacmd; }; -struct cfattach wdsc_ca = { - sizeof(struct sbic_softc), (cfmatch_t)wdscmatch, wdscattach +int wdscmatch(struct device *, void *, void *); +void wdscattach(struct device *, struct device *, void *); + +const struct cfattach wdsc_ca = { + sizeof(struct wdsc_softc), wdscmatch, wdscattach }; struct cfdriver wdsc_cd = { NULL, "wdsc", DV_DULL }; -/* - * Define 'scsi_nosync = 0x00' to enable sync SCSI mode. - * This is untested as yet, use at your own risk... - */ -u_long scsi_nosync = 0xff; -int shift_nosync = 0; +int wdsc_dmasetup(struct wd33c93_softc *, void **, size_t *, int, size_t *); +int wdsc_dmago(struct wd33c93_softc *); +void wdsc_dmastop(struct wd33c93_softc *); + +int wdsc_alloc_physical(struct wdsc_softc *, bus_dmamap_t *, + bus_dma_segment_t *, vaddr_t *, bus_size_t, const char *); +int wdsc_dmaintr(void *); +int wdsc_scsiintr(void *); + +struct scsi_adapter wdsc_switch = { + wd33c93_scsi_cmd, + scsi_minphys, + NULL, + NULL +}; /* * Match for SCSI devices on the onboard WD33C93 chip */ int -wdscmatch(pdp, cdp, auxp) - struct device *pdp; - struct cfdata *cdp; - void *auxp; +wdscmatch(struct device *parent, void *vcf, void *aux) { - /* - * Match everything - */ - return(1); -} + struct confargs *ca = (struct confargs *)aux; + if (strcmp(ca->ca_name, wdsc_cd.cd_name) != 0) + return 0; + return 1; +} /* * Attach the wdsc driver */ void -wdscattach(parent, self, aux) - struct device *parent, *self; - void *aux; +wdscattach(struct device *parent, struct device *self, void *aux) { - struct sbic_softc *sc = (struct sbic_softc *)self; - struct confargs *ca = aux; - struct scsibus_attach_args saa; + struct wdsc_softc *wsc = (struct wdsc_softc *)self; + struct confargs *ca = (struct confargs *)aux; + struct wd33c93_softc *sc = &wsc->sc_wd33c93; int tmp; - printf("\n"); - - sc->sc_enintr = wdsc_enintr; - sc->sc_dmago = wdsc_dmago; - sc->sc_dmanext = wdsc_dmanext; - sc->sc_dmastop = wdsc_dmastop; - sc->sc_dmacmd = 0; - - sc->sc_link.adapter_softc = sc; - sc->sc_link.adapter_target = 7; - sc->sc_link.adapter = &wdsc_scsiswitch; - sc->sc_link.openings = 2; + /* + * Map address and data registers. + */ - sc->sc_sbicp = (sbic_regmap_p)ca->ca_vaddr; + sc->sc_regt = ca->ca_iot; + if (bus_space_map(ca->ca_iot, ca->ca_paddr + 0, 1, 0, + &sc->sc_asr_regh) != 0) { + printf(": failed to map asr register\n"); + return; + } + if (bus_space_map(ca->ca_iot, ca->ca_paddr + 1, 1, 0, + &sc->sc_data_regh) != 0) { + printf(": failed to map data register\n"); + return; + } /* - * Everything is a valid dma address. + * Allocate DMA map for up to MAXPHYS bytes. */ - sc->sc_dmamask = 0; + + wsc->sc_dmat = ca->ca_dmat; + if (bus_dmamap_create(ca->ca_dmat, MAXPHYS, 1 + atop(MAXPHYS), + 0, 0, BUS_DMA_WAITOK, &wsc->sc_dmamap) != 0) { + printf(": failed to create dmamap\n"); + return; + } /* - * The onboard WD33C93 of the '147 is usually clocked at 10MHz... - * (We use 10 times this for accuracy in later calculations) + * Allocate table walk memory. */ - sc->sc_clkfreq = 100; + if (wdsc_alloc_physical(wsc, &wsc->sc_tablemap, &wsc->sc_tableseg, + &wsc->sc_tableva, + sizeof(struct dma_table_entry) * (1 + atop(MAXPHYS)), + "dma table") != 0) { + bus_dmamap_destroy(ca->ca_dmat, wsc->sc_dmamap); + return; + } + + printf("\n"); + + sc->sc_dmasetup = wdsc_dmasetup; + sc->sc_dmago = wdsc_dmago; + sc->sc_dmastop = wdsc_dmastop; + sc->sc_reset = NULL; /* - * Initialize the hardware + * The onboard WD33C93 of the MVME147 is usually clocked at 10MHz... */ - sbicinit(sc); + sc->sc_clkfreq = 100; + sc->sc_id = 7; + sc->sc_dmamode = SBIC_CTL_DMA; - sc->sc_ipl = ca->ca_ipl; + wsc->sc_dmacmd = 0; + wsc->sc_ipl = ca->ca_ipl; sys_pcc->pcc_sbicirq = ca->ca_ipl | PCC_IRQ_INT; sys_pcc->pcc_dmairq = ca->ca_ipl | PCC_IRQ_INT; sys_pcc->pcc_dmacsr = 0; /* - * Fix up the interrupts + * Register interrupt handlers for DMA and WDSC */ - sc->sc_dmaih.ih_fn = wdsc_dmaintr; - sc->sc_dmaih.ih_arg = sc; - sc->sc_dmaih.ih_ipl = ca->ca_ipl; - pccintr_establish(PCCV_DMA, &sc->sc_dmaih, self->dv_xname); + wsc->sc_dmaih.ih_fn = wdsc_dmaintr; + wsc->sc_dmaih.ih_arg = wsc; + wsc->sc_dmaih.ih_ipl = ca->ca_ipl; + pccintr_establish(PCCV_DMA, &wsc->sc_dmaih, self->dv_xname); - sc->sc_sbicih.ih_fn = wdsc_scsiintr; - sc->sc_sbicih.ih_arg = sc; - sc->sc_sbicih.ih_ipl = ca->ca_ipl; - pccintr_establish(PCCV_SBIC, &sc->sc_sbicih, self->dv_xname); - - sys_pcc->pcc_sbicirq = ca->ca_ipl | PCC_IRQ_IEN | PCC_IRQ_INT; + wsc->sc_wdscih.ih_fn = wdsc_scsiintr; + wsc->sc_wdscih.ih_arg = wsc; + wsc->sc_wdscih.ih_ipl = ca->ca_ipl; + pccintr_establish(PCCV_SBIC, &wsc->sc_wdscih, self->dv_xname); /* - * Attach all scsi units on us, watching for boot device + * Attach all SCSI devices on us, watching for boot device * (see device_register). */ - bzero(&saa, sizeof(saa)); - saa.saa_sc_link = &sc->sc_link; - tmp = bootpart; if (ca->ca_paddr != bootaddr) bootpart = -1; - config_found(self, &saa, scsiprint); + wd33c93_attach(sc, &wdsc_switch); bootpart = tmp; /* restore old value */ -} -/* - * Enable DMA interrupts - */ -void -wdsc_enintr(dev) - struct sbic_softc *dev; -{ - dev->sc_flags |= SBICF_INTR; - - sys_pcc->pcc_dmairq = dev->sc_ipl | PCC_IRQ_IEN | PCC_IRQ_INT; + sys_pcc->pcc_sbicirq = ca->ca_ipl | PCC_IRQ_IEN | PCC_IRQ_INT; } /* * Prime the hardware for a DMA transfer */ int -wdsc_dmago(dev, addr, count, flags) - struct sbic_softc *dev; - char *addr; - int count, flags; +wdsc_dmasetup(struct wd33c93_softc *sc, void **addr, size_t *len, int datain, + size_t *dmasize) { - /* - * Set up the command word based on flags - */ - if ((flags & DMAGO_READ) == 0) - dev->sc_dmacmd = DMAC_CSR_ENABLE | DMAC_CSR_WRITE; - else - dev->sc_dmacmd = DMAC_CSR_ENABLE; + struct wdsc_softc *wsc = (struct wdsc_softc *)sc; + int count; + int rc; - dev->sc_flags |= SBICF_INTR; - dev->sc_tcnt = dev->sc_cur->dc_count << 1; + KASSERT((wsc->sc_flags & WDSC_DMA_ACTIVE) == 0); - /* - * Prime the hardware. - * Note, it's probably not necessary to do this here, since dmanext - * is called just prior to the actual transfer. - */ - sys_pcc->pcc_dmacsr = 0; - sys_pcc->pcc_dmairq = dev->sc_ipl | PCC_IRQ_IEN | PCC_IRQ_INT; - sys_pcc->pcc_dmadaddr = (unsigned long)dev->sc_cur->dc_addr; - sys_pcc->pcc_dmabcnt = (unsigned long)dev->sc_tcnt | (1 << 24); - sys_pcc->pcc_dmacsr = dev->sc_dmacmd; + count = *len; + if (count) { + KASSERT((wsc->sc_flags & WDSC_DMA_MAPLOADED) == 0); + + if (datain) + wsc->sc_dmacmd = DMAC_CSR_ENABLE; + else + wsc->sc_dmacmd = DMAC_CSR_ENABLE | DMAC_CSR_WRITE; - return dev->sc_tcnt; + rc = bus_dmamap_load(wsc->sc_dmat, wsc->sc_dmamap, + *addr, count, NULL, BUS_DMA_NOWAIT); + if (rc != 0) + panic("%s: bus_dmamap_load failed, rc=%d", + sc->sc_dev.dv_xname, rc); + + bus_dmamap_sync(wsc->sc_dmat, wsc->sc_dmamap, 0, + wsc->sc_dmamap->dm_mapsize, datain ? + BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); + + /* + * Build dma table, unless transfer fits in one + * contiguous chunk + */ + if (wsc->sc_dmamap->dm_nsegs > 1) { + struct dma_table_entry *entry; + bus_dma_segment_t *seg; + int i; + + entry = (struct dma_table_entry *)wsc->sc_tableva; + seg = wsc->sc_dmamap->dm_segs; + for (i = wsc->sc_dmamap->dm_nsegs; i != 0; i--) { + entry->dc_paddr = seg->ds_addr; + entry->dc_cnt = seg->ds_len | + (FC_SUPERD << 24) | (1UL << 31); + seg++; + entry++; + } + (--entry)->dc_cnt &= ~(1UL << 31); + + wsc->sc_dmacmd |= DMAC_CSR_TABLE; + } + + wsc->sc_flags |= WDSC_DMA_MAPLOADED; + } + + return count; } /* - * Prime the hardware for the next DMA transfer + * Trigger a DMA transfer */ int -wdsc_dmanext(dev) - struct sbic_softc *dev; +wdsc_dmago(struct wd33c93_softc *sc) { - if (dev->sc_cur > dev->sc_last) { - /* - * Shouldn't happen !! - */ - printf("wdsc_dmanext at end !!!\n"); - wdsc_dmastop(dev); - return 0; + struct wdsc_softc *wsc = (struct wdsc_softc *)sc; + + KASSERT((wsc->sc_flags & WDSC_DMA_ACTIVE) == 0); + KASSERT((wsc->sc_flags & WDSC_DMA_MAPLOADED)); + + wsc->sc_flags |= WDSC_DMA_ACTIVE; + + sys_pcc->pcc_dmacsr = 0; + sys_pcc->pcc_dmairq = wsc->sc_ipl | PCC_IRQ_IEN | PCC_IRQ_INT; + if (wsc->sc_dmamap->dm_nsegs > 1) { + sys_pcc->pcc_tafcr = FC_SUPERD; + sys_pcc->pcc_dmataddr = wsc->sc_tableseg.ds_addr; + } else { + sys_pcc->pcc_dmadaddr = + (unsigned long)wsc->sc_dmamap->dm_segs[0].ds_addr; + sys_pcc->pcc_dmabcnt = (FC_SUPERD << 24) | + (unsigned long)wsc->sc_dmamap->dm_segs[0].ds_len; } + sys_pcc->pcc_dmacsr = wsc->sc_dmacmd; - dev->sc_tcnt = dev->sc_cur->dc_count << 1; - - /* - * Load the next DMA address - */ - sys_pcc->pcc_dmacsr = 0; - sys_pcc->pcc_dmairq = dev->sc_ipl | PCC_IRQ_IEN | PCC_IRQ_INT; - sys_pcc->pcc_dmadaddr = (unsigned long)dev->sc_cur->dc_addr; - sys_pcc->pcc_dmabcnt = (unsigned long)dev->sc_tcnt | (1 << 24); - sys_pcc->pcc_dmacsr = dev->sc_dmacmd; - - return dev->sc_tcnt; + return wsc->sc_dmamap->dm_mapsize; } /* - * Stop DMA, and disable interrupts + * Stop DMA, and disable DMA interrupts */ void -wdsc_dmastop(dev) - struct sbic_softc *dev; +wdsc_dmastop(struct wd33c93_softc *sc) { - int s; - - s = splbio(); + struct wdsc_softc *wsc = (struct wdsc_softc *)sc; - sys_pcc->pcc_dmacsr = 0; - sys_pcc->pcc_dmairq = dev->sc_ipl | PCC_IRQ_INT; + if (wsc->sc_flags & WDSC_DMA_ACTIVE) { + sys_pcc->pcc_dmacsr = 0; + sys_pcc->pcc_dmairq = wsc->sc_ipl | PCC_IRQ_INT; - splx(s); + bus_dmamap_sync(wsc->sc_dmat, wsc->sc_dmamap, 0, + wsc->sc_dmamap->dm_mapsize, + wsc->sc_dmacmd & DMAC_CSR_WRITE ? + BUS_DMASYNC_POSTWRITE : BUS_DMASYNC_POSTREAD); + } + if (wsc->sc_flags & WDSC_DMA_MAPLOADED) + bus_dmamap_unload(wsc->sc_dmat, wsc->sc_dmamap); + wsc->sc_flags &= ~(WDSC_DMA_ACTIVE | WDSC_DMA_MAPLOADED); } /* - * Come here following a DMA interrupt + * DMA completion interrupt */ int -wdsc_dmaintr(arg) - void *arg; +wdsc_dmaintr(void *arg) { - struct sbic_softc *dev = (struct sbic_softc *)arg; - int found = 0; + struct wdsc_softc *wsc = (struct wdsc_softc *)arg; + int rc = -1; /* * Really a DMA interrupt? @@ -283,28 +384,31 @@ wdsc_dmaintr(arg) if ((sys_pcc->pcc_dmairq & PCC_IRQ_INT) == 0) return 0; - /* - * Was it a completion interrupt? - * XXXSCW Note: Support for other DMA interrupts is required, eg. buserr - */ if (sys_pcc->pcc_dmacsr & DMAC_CSR_DONE) { - ++found; - - sys_pcc->pcc_dmairq = dev->sc_ipl | PCC_IRQ_IEN | PCC_IRQ_INT; + rc = 1; + /* acknowledge interrupt... */ + sys_pcc->pcc_dmairq = wsc->sc_ipl | PCC_IRQ_IEN | PCC_IRQ_INT; + if (sys_pcc->pcc_dmacsr & (DMAC_CSR_TBUSERR | DMAC_CSR_DBUSERR | + DMAC_CSR_TSIZE | DMAC_CSR_8BITS)) { + printf("%s: DMA error, CSR=%02x\n", + wsc->sc_wd33c93.sc_dev.dv_xname, + sys_pcc->pcc_dmacsr); + } + sys_pcc->pcc_dmacsr = 0; + sys_pcc->pcc_dmairq = wsc->sc_ipl | PCC_IRQ_INT; } - return found; + return rc; } /* - * Come here for SCSI interrupts + * SCSI interrupt */ int -wdsc_scsiintr(arg) - void *arg; +wdsc_scsiintr(void *arg) { - struct sbic_softc *dev = (struct sbic_softc *)arg; - int found; + struct wdsc_softc *wsc = (struct wdsc_softc *)arg; + int rc; /* * Really a SCSI interrupt? @@ -312,15 +416,68 @@ wdsc_scsiintr(arg) if ((sys_pcc->pcc_sbicirq & PCC_IRQ_INT) == 0) return 0; - /* - * Go handle it - */ - found = sbicintr(dev); + rc = wd33c93_intr(&wsc->sc_wd33c93); /* * Acknowledge and clear the interrupt */ - sys_pcc->pcc_sbicirq = dev->sc_ipl | PCC_IRQ_IEN | PCC_IRQ_INT; + sys_pcc->pcc_sbicirq = wsc->sc_ipl | PCC_IRQ_IEN | PCC_IRQ_INT; + + return rc; +} + +/* + * Allocate contiguous physical memory. + */ +int +wdsc_alloc_physical(struct wdsc_softc *wsc, bus_dmamap_t *dmamap, + bus_dma_segment_t *dmaseg, vaddr_t *va, bus_size_t len, const char *what) +{ + int nseg; + int rc; + + len = round_page(len); + + rc = bus_dmamem_alloc(wsc->sc_dmat, len, 0, 0, dmaseg, 1, &nseg, + BUS_DMA_NOWAIT); + if (rc != 0) { + printf("%s: unable to allocate %s memory: error %d\n", + wsc->sc_wd33c93.sc_dev.dv_xname, what, rc); + goto fail1; + } + + rc = bus_dmamem_map(wsc->sc_dmat, dmaseg, nseg, len, + (caddr_t *)va, BUS_DMA_NOWAIT | BUS_DMA_COHERENT); + if (rc != 0) { + printf("%s: unable to map %s memory: error %d\n", + wsc->sc_wd33c93.sc_dev.dv_xname, what, rc); + goto fail2; + } + + rc = bus_dmamap_create(wsc->sc_dmat, len, 1, len, 0, + BUS_DMA_NOWAIT /* | BUS_DMA_ALLOCNOW */, dmamap); + if (rc != 0) { + printf("%s: unable to create %s dma map: error %d\n", + wsc->sc_wd33c93.sc_dev.dv_xname, what, rc); + goto fail3; + } + + rc = bus_dmamap_load(wsc->sc_dmat, *dmamap, (void *)*va, len, NULL, + BUS_DMA_NOWAIT); + if (rc != 0) { + printf("%s: unable to load %s dma map: error %d\n", + wsc->sc_wd33c93.sc_dev.dv_xname, what, rc); + goto fail4; + } + + return 0; - return found; +fail4: + bus_dmamap_destroy(wsc->sc_dmat, *dmamap); +fail3: + bus_dmamem_unmap(wsc->sc_dmat, (caddr_t)*va, PAGE_SIZE); +fail2: + bus_dmamem_free(wsc->sc_dmat, dmaseg, 1); +fail1: + return rc; } |