1 /* $NetBSD: esp_mca.c,v 1.3 2001/12/04 20:51:23 jdolecek Exp $ */ 2 3 /*- 4 * Copyright (c) 2001 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jaromir Dolecek <jdolecek@NetBSD.org>. 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 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* 40 * Driver for NCR 53c90, MCA version, with 86c01 DMA controller chip. 41 * 42 * Some of the information used to write this driver was taken 43 * from Tymm Twillman <tymm@computer.org>'s Linux MCA NC53c90 driver, 44 * in drivers/scsi/mca_53c9x.c 45 */ 46 47 #include <sys/types.h> 48 #include <sys/param.h> 49 #include <sys/systm.h> 50 #include <sys/kernel.h> 51 #include <sys/errno.h> 52 #include <sys/ioctl.h> 53 #include <sys/device.h> 54 #include <sys/buf.h> 55 #include <sys/proc.h> 56 #include <sys/user.h> 57 #include <sys/queue.h> 58 59 #include <dev/scsipi/scsi_all.h> 60 #include <dev/scsipi/scsipi_all.h> 61 #include <dev/scsipi/scsiconf.h> 62 #include <dev/scsipi/scsi_message.h> 63 64 #include <machine/bus.h> 65 #include <machine/cpu.h> 66 67 #include <dev/ic/ncr53c9xreg.h> 68 #include <dev/ic/ncr53c9xvar.h> 69 70 #include <dev/mca/espvar.h> 71 #include <dev/mca/espreg.h> 72 73 #include <dev/mca/mcavar.h> 74 #include <dev/mca/mcareg.h> 75 #include <dev/mca/mcadevs.h> 76 77 #if 0 78 #if defined(DEBUG) && !defined(NCR53C9X_DEBUG) 79 #define NCR53C9X_DEBUG 80 #endif 81 #endif 82 83 #ifdef NCR53C9X_DEBUG 84 static int esp_mca_debug = 0; 85 #define DPRINTF(x) if (esp_mca_debug) printf x; 86 #else 87 #define DPRINTF(x) 88 #endif 89 90 #define ESP_MCA_IOSIZE 0x20 91 #define ESP_REG_OFFSET 0x10 92 93 static void esp_mca_attach __P((struct device *, struct device *, void *)); 94 static int esp_mca_match __P((struct device *, struct cfdata *, void *)); 95 96 /* Linkup to the rest of the kernel */ 97 struct cfattach esp_mca_ca = { 98 sizeof(struct esp_softc), esp_mca_match, esp_mca_attach 99 }; 100 101 /* 102 * Functions and the switch for the MI code. 103 */ 104 static u_char esp_read_reg __P((struct ncr53c9x_softc *, int)); 105 static void esp_write_reg __P((struct ncr53c9x_softc *, int, u_char)); 106 static int esp_dma_isintr __P((struct ncr53c9x_softc *)); 107 static void esp_dma_reset __P((struct ncr53c9x_softc *)); 108 static int esp_dma_intr __P((struct ncr53c9x_softc *)); 109 static int esp_dma_setup __P((struct ncr53c9x_softc *, caddr_t *, 110 size_t *, int, size_t *)); 111 static void esp_dma_go __P((struct ncr53c9x_softc *)); 112 static void esp_dma_stop __P((struct ncr53c9x_softc *)); 113 static int esp_dma_isactive __P((struct ncr53c9x_softc *)); 114 115 static struct ncr53c9x_glue esp_glue = { 116 esp_read_reg, 117 esp_write_reg, 118 esp_dma_isintr, 119 esp_dma_reset, 120 esp_dma_intr, 121 esp_dma_setup, 122 esp_dma_go, 123 esp_dma_stop, 124 esp_dma_isactive, 125 NULL, /* gl_clear_latched_intr */ 126 }; 127 128 static int 129 esp_mca_match(parent, cf, aux) 130 struct device *parent; 131 struct cfdata *cf; 132 void *aux; 133 { 134 struct mca_attach_args *ma = aux; 135 136 switch (ma->ma_id) { 137 case MCA_PRODUCT_NCR53C90: 138 return 1; 139 } 140 141 return 0; 142 } 143 144 static void 145 esp_mca_attach(parent, self, aux) 146 struct device *parent, *self; 147 void *aux; 148 { 149 struct mca_attach_args *ma = aux; 150 struct esp_softc *esc = (void *)self; 151 struct ncr53c9x_softc *sc = &esc->sc_ncr53c9x; 152 u_int16_t iobase; 153 int scsi_id, irq, drq, error; 154 bus_space_handle_t ioh; 155 int pos2, pos3, pos5; 156 157 static const u_int16_t ncrmca_iobase[] = { 158 0, 0x240, 0x340, 0x400, 0x420, 0x3240, 0x8240, 0xa240 159 }; 160 161 /* 162 * NCR SCSI Adapter (ADF 7f4f) 163 * 164 * POS register 2: (adf pos0) 165 * 166 * 7 6 5 4 3 2 1 0 167 * \_/ \___/ \__ enable: 0=adapter disabled, 1=adapter enabled 168 * | \____ I/O base (32B): 001=0x240 010=0x340 011=0x400 169 * | 100=0x420 101=0x3240 110=0x8240 111=0xa240 170 * \__________ IRQ: 00=3 01=5 10=7 11=9 171 * 172 * POS register 3: (adf pos1) 173 * 174 * 7 6 5 4 3 2 1 0 175 * 1 1 1 | \_____/ 176 * | \__ DMA level 177 * \_________ Fairness: 1=enabled 0=disabled 178 * 179 * POS register 5: (adf pos3) 180 * 181 * 7 6 5 4 3 2 1 0 182 * 1 | \___/ 183 * | \__ Static Ram: 0xC8000-0xC87FF + XX*0x4000 184 * \___________ Host Adapter ID: 1=7 0=6 185 */ 186 187 pos2 = mca_conf_read(ma->ma_mc, ma->ma_slot, 2); 188 pos3 = mca_conf_read(ma->ma_mc, ma->ma_slot, 3); 189 pos5 = mca_conf_read(ma->ma_mc, ma->ma_slot, 5); 190 191 iobase = ncrmca_iobase[(pos2 & 0x0e) >> 1]; 192 irq = 3 + 2*((pos2 & 0x30) >> 4); 193 drq = (pos3 & 0x0f); 194 scsi_id = 6 + ((pos5 & 0x20) ? 1 : 0); 195 196 printf(" slot %d irq %d drq %d: NCR SCSI Adapter\n", 197 ma->ma_slot + 1, irq, drq); 198 199 /* Map the 86C01 registers */ 200 if (bus_space_map(ma->ma_iot, iobase, ESP_MCA_IOSIZE, 0, &ioh)) { 201 printf("%s: can't map i/o space\n", sc->sc_dev.dv_xname); 202 return; 203 } 204 205 esc->sc_iot = ma->ma_iot; 206 esc->sc_ioh = ioh; 207 208 /* Submap the 'esp' registers */ 209 if (bus_space_subregion(ma->ma_iot, ioh, ESP_REG_OFFSET, 210 ESP_MCA_IOSIZE-ESP_REG_OFFSET, &esc->sc_esp_ioh)) { 211 printf("%s: can't subregion i/o space\n", sc->sc_dev.dv_xname); 212 return; 213 } 214 215 /* Setup DMA map */ 216 esc->sc_dmat = ma->ma_dmat; 217 if ((error = mca_dmamap_create(esc->sc_dmat, MAXPHYS, 218 BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW | MCABUS_DMA_IOPORT, 219 &esc->sc_xfer, drq)) != 0){ 220 printf("%s: couldn't create DMA map - error %d\n", 221 sc->sc_dev.dv_xname, error); 222 return; 223 } 224 225 /* MI code glue */ 226 sc->sc_id = scsi_id; 227 sc->sc_freq = 25; /* Mhz */ 228 229 sc->sc_glue = &esp_glue; 230 231 sc->sc_cfg1 = sc->sc_id | NCRCFG1_PARENB; //| NCRCFG1_SLOW; 232 /* No point setting sc_cfg[2345], they won't be used */ 233 234 sc->sc_rev = NCR_VARIANT_NCR53C90_86C01; 235 sc->sc_minsync = 0; 236 237 /* max 64KB DMA */ 238 sc->sc_maxxfer = 64 * 1024; 239 240 /* Establish interrupt */ 241 esc->sc_ih = mca_intr_establish(ma->ma_mc, irq, IPL_BIO, ncr53c9x_intr, 242 esc); 243 if (esc->sc_ih == NULL) { 244 printf("%s: couldn't establish interrupt\n", 245 sc->sc_dev.dv_xname); 246 return; 247 } 248 249 /* 250 * Massage the 86C01 chip - setup MCA DMA controller for DMA via 251 * the 86C01 register, and enable 86C01 interrupts. 252 */ 253 mca_dma_set_ioport(drq, iobase + N86C01_PIO); 254 255 bus_space_write_1(esc->sc_iot, esc->sc_ioh, N86C01_MODE_ENABLE, 256 bus_space_read_1(esc->sc_iot, esc->sc_ioh, N86C01_MODE_ENABLE) 257 | N86C01_INTR_ENABLE); 258 259 /* 260 * Now try to attach all the sub-devices 261 */ 262 sc->sc_adapter.adapt_minphys = minphys; 263 sc->sc_adapter.adapt_request = ncr53c9x_scsipi_request; 264 265 /* Do the common parts of attachment. */ 266 printf("%s", sc->sc_dev.dv_xname); 267 ncr53c9x_attach(sc); 268 } 269 270 /* 271 * Glue functions. 272 */ 273 274 static u_char 275 esp_read_reg(sc, reg) 276 struct ncr53c9x_softc *sc; 277 int reg; 278 { 279 struct esp_softc *esc = (struct esp_softc *)sc; 280 281 return (bus_space_read_1(esc->sc_iot, esc->sc_esp_ioh, reg)); 282 } 283 284 static void 285 esp_write_reg(sc, reg, val) 286 struct ncr53c9x_softc *sc; 287 int reg; 288 u_char val; 289 { 290 struct esp_softc *esc = (struct esp_softc *)sc; 291 292 bus_space_write_1(esc->sc_iot, esc->sc_esp_ioh, reg, val); 293 } 294 295 static int 296 esp_dma_isintr(sc) 297 struct ncr53c9x_softc *sc; 298 { 299 struct esp_softc *esc = (struct esp_softc *)sc; 300 301 DPRINTF(("[esp_dma_isintr] ")); 302 return (bus_space_read_1(esc->sc_iot, esc->sc_ioh, 303 N86C01_STATUS) & N86C01_IRQ_PEND); 304 } 305 306 static void 307 esp_dma_reset(sc) 308 struct ncr53c9x_softc *sc; 309 { 310 struct esp_softc *esc = (struct esp_softc *)sc; 311 312 DPRINTF(("[esp_dma_reset] ")); 313 314 if (esc->sc_flags & ESP_XFER_LOADED) { 315 bus_dmamap_unload(esc->sc_dmat, esc->sc_xfer); 316 esc->sc_flags &= ~ESP_XFER_LOADED; 317 } 318 319 if (esc->sc_flags & ESP_XFER_ACTIVE) { 320 esc->sc_flags &= ~ESP_XFER_ACTIVE; 321 mca_disk_unbusy(); 322 } 323 } 324 325 static int 326 esp_dma_intr(sc) 327 struct ncr53c9x_softc *sc; 328 { 329 struct esp_softc *esc = (struct esp_softc *) sc; 330 DPRINTF(("[esp_dma_intr] ")); 331 332 if ((esc->sc_flags & ESP_XFER_ACTIVE) == 0) { 333 printf("%s: dma_intr--inactive DMA\n", sc->sc_dev.dv_xname); 334 return (-1); 335 } 336 337 if ((sc->sc_espintr & NCRINTR_BS) == 0) { 338 esc->sc_flags &= ~ESP_XFER_ACTIVE; 339 mca_disk_unbusy(); 340 return (0); 341 } 342 343 sc->sc_espstat |= NCRSTAT_TC; /* XXX */ 344 345 if ((sc->sc_espstat & NCRSTAT_TC) == 0) { 346 printf("%s: DMA not complete?\n", sc->sc_dev.dv_xname); 347 return (1); 348 } 349 350 bus_dmamap_sync(esc->sc_dmat, esc->sc_xfer, 0, 351 *esc->sc_xfer_len, 352 (esc->sc_flags & ESP_XFER_READ) 353 ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); 354 355 bus_dmamap_unload(esc->sc_dmat, esc->sc_xfer); 356 esc->sc_flags &= ~ESP_XFER_LOADED; 357 358 *esc->sc_xfer_addr += *esc->sc_xfer_len; 359 *esc->sc_xfer_len = 0; 360 361 esc->sc_flags &= ~ESP_XFER_ACTIVE; 362 mca_disk_unbusy(); 363 364 return (0); 365 } 366 367 /* 368 * Setup DMA transfer. 369 */ 370 static int 371 esp_dma_setup(sc, addr, len, datain, dmasize) 372 struct ncr53c9x_softc *sc; 373 caddr_t *addr; 374 size_t *len; 375 int datain; 376 size_t *dmasize; 377 { 378 struct esp_softc *esc = (struct esp_softc *) sc; 379 int error; 380 int fl; 381 382 DPRINTF(("[esp_dma_setup] ")); 383 384 if (esc->sc_flags & ESP_XFER_LOADED) { 385 printf("%s: esp_dma_setup: unloading leaked xfer\n", 386 sc->sc_dev.dv_xname); 387 bus_dmamap_unload(esc->sc_dmat, esc->sc_xfer); 388 esc->sc_flags &= ~ESP_XFER_LOADED; 389 } 390 391 /* Load the buffer for DMA transfer. */ 392 fl = (datain) ? BUS_DMA_READ : BUS_DMA_WRITE; 393 394 if ((error = bus_dmamap_load(esc->sc_dmat, esc->sc_xfer, *addr, 395 *len, NULL, BUS_DMA_STREAMING|fl))) { 396 printf("%s: esp_dma_setup: unable to load DMA buffer - error %d\n", 397 sc->sc_dev.dv_xname, error); 398 return (error); 399 } 400 401 bus_dmamap_sync(esc->sc_dmat, esc->sc_xfer, 0, 402 *len, (datain) ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); 403 404 esc->sc_flags |= ESP_XFER_LOADED | (datain ? ESP_XFER_READ : 0); 405 esc->sc_xfer_addr = addr; 406 esc->sc_xfer_len = len; 407 408 return (0); 409 } 410 411 static void 412 esp_dma_go(sc) 413 struct ncr53c9x_softc *sc; 414 { 415 struct esp_softc *esc = (struct esp_softc *) sc; 416 DPRINTF(("[esp_dma_go] ")); 417 418 esc->sc_flags |= ESP_XFER_ACTIVE; 419 mca_disk_busy(); 420 } 421 422 static void 423 esp_dma_stop(sc) 424 struct ncr53c9x_softc *sc; 425 { 426 DPRINTF(("[esp_dma_stop] ")); 427 428 panic("%s: stop not yet implemented", sc->sc_dev.dv_xname); 429 } 430 431 static int 432 esp_dma_isactive(sc) 433 struct ncr53c9x_softc *sc; 434 { 435 struct esp_softc *esc = (struct esp_softc *) sc; 436 DPRINTF(("[esp_dma_isactive] ")); 437 438 return (esc->sc_flags & ESP_XFER_ACTIVE); 439 } 440