1 /* $NetBSD: wdsc.c,v 1.21 2001/05/31 18:46:08 scw Exp $ */ 2 3 /* 4 * Copyright (c) 1996 Steve Woodford 5 * Copyright (c) 1982, 1990 The Regents of the University of California. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * @(#)wdsc.c 37 */ 38 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/kernel.h> 42 #include <sys/device.h> 43 44 #include <dev/scsipi/scsi_all.h> 45 #include <dev/scsipi/scsipi_all.h> 46 #include <dev/scsipi/scsiconf.h> 47 48 #include <machine/cpu.h> 49 #include <machine/bus.h> 50 #include <machine/autoconf.h> 51 52 #include <mvme68k/dev/dmavar.h> 53 #include <mvme68k/dev/pccreg.h> 54 #include <mvme68k/dev/pccvar.h> 55 #include <mvme68k/dev/sbicreg.h> 56 #include <mvme68k/dev/sbicvar.h> 57 #include <mvme68k/dev/wdscreg.h> 58 59 void wdsc_pcc_attach __P((struct device *, struct device *, void *)); 60 int wdsc_pcc_match __P((struct device *, struct cfdata *, void *)); 61 62 struct cfattach wdsc_pcc_ca = { 63 sizeof(struct sbic_softc), wdsc_pcc_match, wdsc_pcc_attach 64 }; 65 66 extern struct cfdriver wdsc_cd; 67 68 void wdsc_enintr __P((struct sbic_softc *)); 69 int wdsc_dmago __P((struct sbic_softc *, char *, int, int)); 70 int wdsc_dmanext __P((struct sbic_softc *)); 71 void wdsc_dmastop __P((struct sbic_softc *)); 72 int wdsc_dmaintr __P((void *)); 73 int wdsc_scsiintr __P((void *)); 74 75 /* 76 * Match for SCSI devices on the onboard WD33C93 chip 77 */ 78 int 79 wdsc_pcc_match(pdp, cf, auxp) 80 struct device *pdp; 81 struct cfdata *cf; 82 void *auxp; 83 { 84 struct pcc_attach_args *pa = auxp; 85 86 if (strcmp(pa->pa_name, wdsc_cd.cd_name)) 87 return (0); 88 89 pa->pa_ipl = cf->pcccf_ipl; 90 return (1); 91 } 92 93 /* 94 * Attach the wdsc driver 95 */ 96 void 97 wdsc_pcc_attach(pdp, dp, auxp) 98 struct device *pdp, *dp; 99 void *auxp; 100 { 101 struct sbic_softc *sc; 102 struct pcc_attach_args *pa; 103 bus_space_handle_t bush; 104 static struct evcnt evcnt; /* XXXSCW: Temporary hack */ 105 106 sc = (struct sbic_softc *)dp; 107 pa = auxp; 108 109 bus_space_map(pa->pa_bust, pa->pa_offset, 0x20, 0, &bush); 110 111 /* 112 * XXXSCW: We *need* an MI, bus_spaced WD33C93 driver... 113 */ 114 sc->sc_sbicp = (sbic_regmap_p) bush; 115 116 sc->sc_driver = (void *) &evcnt; 117 sc->sc_enintr = wdsc_enintr; 118 sc->sc_dmago = wdsc_dmago; 119 sc->sc_dmanext = wdsc_dmanext; 120 sc->sc_dmastop = wdsc_dmastop; 121 sc->sc_dmacmd = 0; 122 123 sc->sc_adapter.adapt_dev = &sc->sc_dev; 124 sc->sc_adapter.adapt_nchannels = 1; 125 sc->sc_adapter.adapt_openings = 7; 126 sc->sc_adapter.adapt_max_periph = 1; 127 sc->sc_adapter.adapt_ioctl = NULL; 128 sc->sc_adapter.adapt_minphys = sbic_minphys; 129 sc->sc_adapter.adapt_request = sbic_scsi_request; 130 131 sc->sc_channel.chan_adapter = &sc->sc_adapter; 132 sc->sc_channel.chan_bustype = &scsi_bustype; 133 sc->sc_channel.chan_channel = 0; 134 sc->sc_channel.chan_ntargets = 8; 135 sc->sc_channel.chan_nluns = 8; 136 sc->sc_channel.chan_id = 7; 137 138 printf(": WD33C93 SCSI, target %d\n", sc->sc_channel.chan_id); 139 140 /* 141 * Eveything is a valid dma address. 142 */ 143 sc->sc_dmamask = 0; 144 145 /* 146 * The onboard WD33C93 of the '147 is usually clocked at 10MHz... 147 * (We use 10 times this for accuracy in later calculations) 148 */ 149 sc->sc_clkfreq = 100; 150 151 /* 152 * Initialise the hardware 153 */ 154 sbicinit(sc); 155 156 /* 157 * Fix up the interrupts 158 */ 159 sc->sc_ipl = pa->pa_ipl & PCC_IMASK; 160 161 pcc_reg_write(sys_pcc, PCCREG_SCSI_INTR_CTRL, PCC_ICLEAR); 162 pcc_reg_write(sys_pcc, PCCREG_DMA_INTR_CTRL, PCC_ICLEAR); 163 pcc_reg_write(sys_pcc, PCCREG_DMA_CONTROL, 0); 164 165 evcnt_attach_dynamic(&evcnt, EVCNT_TYPE_INTR, pccintr_evcnt(sc->sc_ipl), 166 "disk", sc->sc_dev.dv_xname); 167 pccintr_establish(PCCV_DMA, wdsc_dmaintr, sc->sc_ipl, sc, &evcnt); 168 pccintr_establish(PCCV_SCSI, wdsc_scsiintr, sc->sc_ipl, sc, &evcnt); 169 pcc_reg_write(sys_pcc, PCCREG_SCSI_INTR_CTRL, 170 sc->sc_ipl | PCC_IENABLE | PCC_ICLEAR); 171 172 (void)config_found(dp, &sc->sc_channel, scsiprint); 173 } 174 175 /* 176 * Enable DMA interrupts 177 */ 178 void 179 wdsc_enintr(dev) 180 struct sbic_softc *dev; 181 { 182 dev->sc_flags |= SBICF_INTR; 183 184 pcc_reg_write(sys_pcc, PCCREG_DMA_INTR_CTRL, 185 dev->sc_ipl | PCC_IENABLE | PCC_ICLEAR); 186 } 187 188 /* 189 * Prime the hardware for a DMA transfer 190 */ 191 int 192 wdsc_dmago(dev, addr, count, flags) 193 struct sbic_softc *dev; 194 char *addr; 195 int count, flags; 196 { 197 /* 198 * Set up the command word based on flags 199 */ 200 if ( (flags & DMAGO_READ) == 0 ) 201 dev->sc_dmacmd = DMAC_CSR_ENABLE | DMAC_CSR_WRITE; 202 else 203 dev->sc_dmacmd = DMAC_CSR_ENABLE; 204 205 dev->sc_flags |= SBICF_INTR; 206 dev->sc_tcnt = dev->sc_cur->dc_count << 1; 207 208 /* 209 * Prime the hardware. 210 * Note, it's probably not necessary to do this here, since dmanext 211 * is called just prior to the actual transfer. 212 */ 213 pcc_reg_write(sys_pcc, PCCREG_DMA_CONTROL, 0); 214 pcc_reg_write(sys_pcc, PCCREG_DMA_INTR_CTRL, 215 dev->sc_ipl | PCC_IENABLE | PCC_ICLEAR); 216 pcc_reg_write32(sys_pcc, PCCREG_DMA_DATA_ADDR, 217 (u_int32_t) dev->sc_cur->dc_addr); 218 pcc_reg_write32(sys_pcc, PCCREG_DMA_BYTE_COUNT, 219 (u_int32_t) dev->sc_tcnt | (1 << 24)); 220 pcc_reg_write(sys_pcc, PCCREG_DMA_CONTROL, dev->sc_dmacmd); 221 222 return(dev->sc_tcnt); 223 } 224 225 /* 226 * Prime the hardware for the next DMA transfer 227 */ 228 int 229 wdsc_dmanext(dev) 230 struct sbic_softc *dev; 231 { 232 if ( dev->sc_cur > dev->sc_last ) { 233 /* 234 * Shouldn't happen !! 235 */ 236 printf("wdsc_dmanext at end !!!\n"); 237 wdsc_dmastop(dev); 238 return(0); 239 } 240 241 dev->sc_tcnt = dev->sc_cur->dc_count << 1; 242 243 /* 244 * Load the next DMA address 245 */ 246 pcc_reg_write(sys_pcc, PCCREG_DMA_CONTROL, 0); 247 pcc_reg_write(sys_pcc, PCCREG_DMA_INTR_CTRL, 248 dev->sc_ipl | PCC_IENABLE | PCC_ICLEAR); 249 pcc_reg_write32(sys_pcc, PCCREG_DMA_DATA_ADDR, 250 (u_int32_t) dev->sc_cur->dc_addr); 251 pcc_reg_write32(sys_pcc, PCCREG_DMA_BYTE_COUNT, 252 (u_int32_t) dev->sc_tcnt | (1 << 24)); 253 pcc_reg_write(sys_pcc, PCCREG_DMA_CONTROL, dev->sc_dmacmd); 254 255 return(dev->sc_tcnt); 256 } 257 258 /* 259 * Stop DMA, and disable interrupts 260 */ 261 void 262 wdsc_dmastop(dev) 263 struct sbic_softc *dev; 264 { 265 int s; 266 267 s = splbio(); 268 269 pcc_reg_write(sys_pcc, PCCREG_DMA_CONTROL, 0); 270 pcc_reg_write(sys_pcc, PCCREG_DMA_INTR_CTRL, dev->sc_ipl | PCC_ICLEAR); 271 272 splx(s); 273 } 274 275 /* 276 * Come here following a DMA interrupt 277 */ 278 int 279 wdsc_dmaintr(arg) 280 void *arg; 281 { 282 struct sbic_softc *dev = arg; 283 int found = 0; 284 285 /* 286 * Really a DMA interrupt? 287 */ 288 if ( (pcc_reg_read(sys_pcc, PCCREG_DMA_INTR_CTRL) & 0x80) == 0 ) 289 return(0); 290 291 /* 292 * Was it a completion interrupt? 293 * XXXSCW Note: Support for other DMA interrupts is required, eg. buserr 294 */ 295 if ( pcc_reg_read(sys_pcc, PCCREG_DMA_CONTROL) & DMAC_CSR_DONE ) { 296 ++found; 297 298 pcc_reg_write(sys_pcc, PCCREG_DMA_INTR_CTRL, 299 dev->sc_ipl | PCC_IENABLE | PCC_ICLEAR); 300 } 301 302 return(found); 303 } 304 305 /* 306 * Come here for SCSI interrupts 307 */ 308 int 309 wdsc_scsiintr(arg) 310 void *arg; 311 { 312 struct sbic_softc *dev = arg; 313 int found; 314 315 /* 316 * Really a SCSI interrupt? 317 */ 318 if ( (pcc_reg_read(sys_pcc, PCCREG_SCSI_INTR_CTRL) & 0x80) == 0 ) 319 return(0); 320 321 /* 322 * Go handle it 323 */ 324 found = sbicintr(dev); 325 326 /* 327 * Acknowledge and clear the interrupt 328 */ 329 pcc_reg_write(sys_pcc, PCCREG_SCSI_INTR_CTRL, 330 dev->sc_ipl | PCC_IENABLE | PCC_ICLEAR); 331 332 return(found); 333 } 334