1 /* $NetBSD: hpcdma.c,v 1.15 2008/04/28 20:23:34 martin Exp $ */ 2 3 /* 4 * Copyright (c) 2001 Wayne Knowles 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Wayne Knowles 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Support for SCSI DMA provided by the HPC. 34 * 35 * Note: We use SCSI0 offsets, etc. here. Since the layout of SCSI0 36 * and SCSI1 are the same, this is no problem. 37 */ 38 39 #include <sys/cdefs.h> 40 __KERNEL_RCSID(0, "$NetBSD: hpcdma.c,v 1.15 2008/04/28 20:23:34 martin Exp $"); 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/device.h> 45 #include <sys/buf.h> 46 47 #include <uvm/uvm_extern.h> 48 49 #include <machine/bus.h> 50 51 #include <sgimips/hpc/hpcvar.h> 52 #include <sgimips/hpc/hpcreg.h> 53 #include <sgimips/hpc/hpcdma.h> 54 55 /* 56 * Allocate DMA Chain descriptor list 57 */ 58 void 59 hpcdma_init(struct hpc_attach_args *haa, struct hpc_dma_softc *sc, int ndesc) 60 { 61 bus_dma_segment_t seg; 62 int rseg, allocsz; 63 64 sc->sc_bst = haa->ha_st; 65 sc->sc_dmat = haa->ha_dmat; 66 sc->sc_ndesc = ndesc; 67 sc->sc_flags = 0; 68 69 if (bus_space_subregion(haa->ha_st, haa->ha_sh, haa->ha_dmaoff, 70 sc->hpc->scsi0_regs_size, &sc->sc_bsh) != 0) { 71 printf(": can't map DMA registers\n"); 72 return; 73 } 74 75 /* Alloc 1 additional descriptor - needed for DMA bug fix */ 76 allocsz = sizeof(struct hpc_dma_desc) * (ndesc + 1); 77 KASSERT(allocsz <= PAGE_SIZE); 78 79 if (bus_dmamap_create(sc->sc_dmat, PAGE_SIZE, 1 /*seg*/, 80 PAGE_SIZE, 0, BUS_DMA_WAITOK, 81 &sc->sc_dmamap) != 0) { 82 printf(": failed to create dmamap\n"); 83 return; 84 } 85 86 /* 87 * Allocate a block of memory for dma chaining pointers 88 */ 89 if (bus_dmamem_alloc(sc->sc_dmat, allocsz, 0, 0, 90 &seg, 1, &rseg, BUS_DMA_NOWAIT)) { 91 printf(": can't allocate sglist\n"); 92 return; 93 } 94 /* Map pages into kernel memory */ 95 if (bus_dmamem_map(sc->sc_dmat, &seg, rseg, allocsz, 96 (void **)&sc->sc_desc_kva, BUS_DMA_NOWAIT)) { 97 printf(": can't map sglist\n"); 98 bus_dmamem_free(sc->sc_dmat, &seg, rseg); 99 return; 100 } 101 102 if (bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_desc_kva, 103 allocsz, NULL, BUS_DMA_NOWAIT)) { 104 printf(": can't load sglist\n"); 105 return; 106 } 107 108 sc->sc_desc_pa = (void *) (vaddr_t)sc->sc_dmamap->dm_segs[0].ds_addr; 109 } 110 111 112 void 113 hpcdma_sglist_create(struct hpc_dma_softc *sc, bus_dmamap_t dmamap) 114 { 115 struct hpc_dma_desc *hva, *hpa; 116 bus_dma_segment_t *segp; 117 int i; 118 119 KASSERT(dmamap->dm_nsegs <= sc->sc_ndesc); 120 121 hva = sc->sc_desc_kva; 122 hpa = sc->sc_desc_pa; 123 segp = dmamap->dm_segs; 124 125 #ifdef DMA_DEBUG 126 printf("DMA_SGLIST<"); 127 #endif 128 for (i = dmamap->dm_nsegs; i; i--) { 129 #ifdef DMA_DEBUG 130 printf("%p:%ld, ", (void *)segp->ds_addr, segp->ds_len); 131 #endif 132 if (sc->hpc->revision == 3) { 133 hva->hpc3_hdd_bufptr = segp->ds_addr; 134 hva->hpc3_hdd_ctl = segp->ds_len; 135 hva->hdd_descptr = (u_int32_t) ++hpa; 136 } else /* HPC 1/1.5 */ { 137 /* there doesn't seem to be any good way of doing this 138 via an abstraction layer */ 139 hva->hpc1_hdd_bufptr = segp->ds_addr; 140 hva->hpc1_hdd_ctl = segp->ds_len; 141 hva->hdd_descptr = (u_int32_t) ++hpa; 142 } 143 ++hva; ++segp; 144 } 145 146 /* Work around HPC3 DMA bug */ 147 if (sc->hpc->revision == 3) 148 { 149 hva->hpc3_hdd_bufptr = 0; 150 hva->hpc3_hdd_ctl = HPC3_HDD_CTL_EOCHAIN; 151 hva->hdd_descptr = 0; 152 hva++; 153 } else { 154 hva--; 155 hva->hpc1_hdd_bufptr |= HPC1_HDD_CTL_EOCHAIN; 156 hva->hdd_descptr = 0; 157 } 158 159 #ifdef DMA_DEBUG 160 printf(">\n"); 161 #endif 162 bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, 163 0, sc->sc_dmamap->dm_mapsize, 164 BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); 165 166 /* Load DMA Descriptor list */ 167 bus_space_write_4(sc->sc_bst, sc->sc_bsh, sc->hpc->scsi0_ndbp, 168 (u_int32_t)sc->sc_desc_pa); 169 } 170 171 void 172 hpcdma_cntl(struct hpc_dma_softc *sc, uint32_t mode) 173 { 174 175 bus_space_write_4(sc->sc_bst, sc->sc_bsh, sc->hpc->scsi0_ctl, mode); 176 } 177 178 void 179 hpcdma_reset(struct hpc_dma_softc *sc) 180 { 181 182 bus_space_write_4(sc->sc_bst, sc->sc_bsh, sc->hpc->scsi0_ctl, 183 sc->hpc->scsi_dmactl_reset); 184 delay(100); 185 bus_space_write_4(sc->sc_bst, sc->sc_bsh, sc->hpc->scsi0_ctl, 0); 186 delay(1000); 187 } 188 189 void 190 hpcdma_flush(struct hpc_dma_softc *sc) 191 { 192 u_int32_t mode; 193 194 mode = bus_space_read_4(sc->sc_bst, sc->sc_bsh, sc->hpc->scsi0_ctl); 195 bus_space_write_4(sc->sc_bst, sc->sc_bsh, sc->hpc->scsi0_ctl, 196 mode | sc->hpc->scsi_dmactl_flush); 197 198 /* Wait for Active bit to drop */ 199 while (bus_space_read_4(sc->sc_bst, sc->sc_bsh, sc->hpc->scsi0_ctl) & 200 sc->hpc->scsi_dmactl_active) { 201 bus_space_barrier(sc->sc_bst, sc->sc_bsh, sc->hpc->scsi0_ctl, 4, 202 BUS_SPACE_BARRIER_READ); 203 } 204 } 205