Annotation of sys/arch/mvme88k/dev/vs.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: vs.c,v 1.64 2006/12/01 19:36:09 miod Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 2004, Miodrag Vallat.
! 5: * Copyright (c) 1999 Steve Murphree, Jr.
! 6: * Copyright (c) 1990 The Regents of the University of California.
! 7: * All rights reserved.
! 8: *
! 9: * This code is derived from software contributed to Berkeley by
! 10: * Van Jacobson of Lawrence Berkeley Laboratory.
! 11: *
! 12: * Redistribution and use in source and binary forms, with or without
! 13: * modification, are permitted provided that the following conditions
! 14: * are met:
! 15: * 1. Redistributions of source code must retain the above copyright
! 16: * notice, this list of conditions and the following disclaimer.
! 17: * 2. Redistributions in binary form must reproduce the above copyright
! 18: * notice, this list of conditions and the following disclaimer in the
! 19: * documentation and/or other materials provided with the distribution.
! 20: * 3. 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:
! 37: /*
! 38: * MVME328S SCSI adaptor driver
! 39: */
! 40:
! 41: /* This card lives in D16 space */
! 42: #define __BUS_SPACE_RESTRICT_D16__
! 43:
! 44: #include <sys/param.h>
! 45: #include <sys/systm.h>
! 46: #include <sys/device.h>
! 47: #include <sys/disklabel.h>
! 48: #include <sys/dkstat.h>
! 49: #include <sys/buf.h>
! 50: #include <sys/malloc.h>
! 51:
! 52: #include <uvm/uvm_extern.h>
! 53:
! 54: #include <scsi/scsi_all.h>
! 55: #include <scsi/scsiconf.h>
! 56:
! 57: #include <machine/autoconf.h>
! 58: #include <machine/cmmu.h>
! 59: #include <machine/cpu.h>
! 60:
! 61: #include <mvme88k/dev/vsreg.h>
! 62: #include <mvme88k/dev/vsvar.h>
! 63: #include <mvme88k/dev/vme.h>
! 64:
! 65: int vsmatch(struct device *, void *, void *);
! 66: void vsattach(struct device *, struct device *, void *);
! 67: int vs_scsicmd(struct scsi_xfer *);
! 68:
! 69: struct scsi_adapter vs_scsiswitch = {
! 70: vs_scsicmd,
! 71: minphys,
! 72: 0, /* no lun support */
! 73: 0, /* no lun support */
! 74: };
! 75:
! 76: struct scsi_device vs_scsidev = {
! 77: NULL, /* use default error handler */
! 78: NULL, /* do not have a start function */
! 79: NULL, /* have no async handler */
! 80: NULL, /* Use default done routine */
! 81: };
! 82:
! 83: struct cfattach vs_ca = {
! 84: sizeof(struct vs_softc), vsmatch, vsattach,
! 85: };
! 86:
! 87: struct cfdriver vs_cd = {
! 88: NULL, "vs", DV_DULL,
! 89: };
! 90:
! 91: int do_vspoll(struct vs_softc *, struct scsi_xfer *, int);
! 92: void thaw_queue(struct vs_softc *, int);
! 93: void thaw_all_queues(struct vs_softc *);
! 94: M328_SG vs_alloc_scatter_gather(void);
! 95: M328_SG vs_build_memory_structure(struct vs_softc *, struct scsi_xfer *,
! 96: bus_addr_t);
! 97: void vs_chksense(struct scsi_xfer *);
! 98: void vs_dealloc_scatter_gather(M328_SG);
! 99: int vs_eintr(void *);
! 100: bus_addr_t vs_getcqe(struct vs_softc *);
! 101: bus_addr_t vs_getiopb(struct vs_softc *);
! 102: int vs_initialize(struct vs_softc *);
! 103: int vs_intr(struct vs_softc *);
! 104: void vs_link_sg_element(sg_list_element_t *, vaddr_t, int);
! 105: void vs_link_sg_list(sg_list_element_t *, vaddr_t, int);
! 106: int vs_nintr(void *);
! 107: int vs_poll(struct vs_softc *, struct vs_cb *);
! 108: void vs_print_addr(struct vs_softc *, struct scsi_xfer *);
! 109: struct vs_cb *vs_find_queue(struct scsi_link *, struct vs_softc *);
! 110: void vs_reset(struct vs_softc *, int);
! 111: void vs_resync(struct vs_softc *);
! 112: void vs_scsidone(struct vs_softc *, struct vs_cb *);
! 113:
! 114: static __inline__ void vs_free(struct vs_cb *);
! 115: static __inline__ void vs_clear_return_info(struct vs_softc *);
! 116: static __inline__ paddr_t kvtop(vaddr_t);
! 117:
! 118: int
! 119: vsmatch(struct device *device, void *cf, void *args)
! 120: {
! 121: struct confargs *ca = args;
! 122: bus_space_tag_t iot = ca->ca_iot;
! 123: bus_space_handle_t ioh;
! 124: int rc;
! 125:
! 126: if (bus_space_map(iot, ca->ca_paddr, S_SHORTIO, 0, &ioh) != 0)
! 127: return 0;
! 128: rc = badaddr((vaddr_t)bus_space_vaddr(iot, ioh), 1);
! 129: bus_space_unmap(iot, ioh, S_SHORTIO);
! 130:
! 131: return rc == 0;
! 132: }
! 133:
! 134: void
! 135: vsattach(struct device *parent, struct device *self, void *args)
! 136: {
! 137: struct vs_softc *sc = (struct vs_softc *)self;
! 138: struct confargs *ca = args;
! 139: struct scsi_link *sc_link;
! 140: struct scsibus_attach_args saa;
! 141: int evec, bus;
! 142: int tmp;
! 143:
! 144: /* get the next available vector for the error interrupt */
! 145: evec = vme_findvec(ca->ca_vec);
! 146:
! 147: if (ca->ca_vec < 0 || evec < 0) {
! 148: printf(": no more interrupts!\n");
! 149: return;
! 150: }
! 151: if (ca->ca_ipl < 0)
! 152: ca->ca_ipl = IPL_BIO;
! 153:
! 154: printf(" vec 0x%x: ", evec);
! 155:
! 156: sc->sc_paddr = ca->ca_paddr;
! 157: sc->sc_iot = ca->ca_iot;
! 158: if (bus_space_map(sc->sc_iot, sc->sc_paddr, S_SHORTIO, 0,
! 159: &sc->sc_ioh) != 0) {
! 160: printf("can't map registers!\n");
! 161: return;
! 162: }
! 163:
! 164: sc->sc_ipl = ca->ca_ipl;
! 165: sc->sc_nvec = ca->ca_vec;
! 166: sc->sc_evec = evec;
! 167:
! 168: if (vs_initialize(sc))
! 169: return;
! 170:
! 171: sc->sc_ih_n.ih_fn = vs_nintr;
! 172: sc->sc_ih_n.ih_arg = sc;
! 173: sc->sc_ih_n.ih_wantframe = 0;
! 174: sc->sc_ih_n.ih_ipl = ca->ca_ipl;
! 175:
! 176: sc->sc_ih_e.ih_fn = vs_eintr;
! 177: sc->sc_ih_e.ih_arg = sc;
! 178: sc->sc_ih_e.ih_wantframe = 0;
! 179: sc->sc_ih_e.ih_ipl = ca->ca_ipl;
! 180:
! 181: vmeintr_establish(sc->sc_nvec, &sc->sc_ih_n, self->dv_xname);
! 182: snprintf(sc->sc_intrname_e, sizeof sc->sc_intrname_e,
! 183: "%s_err", self->dv_xname);
! 184: vmeintr_establish(sc->sc_evec, &sc->sc_ih_e, sc->sc_intrname_e);
! 185:
! 186: printf("SCSI ID");
! 187:
! 188: for (bus = 0; bus < 2; bus++) {
! 189: if (sc->sc_id[bus] < 0)
! 190: continue;
! 191:
! 192: sc_link = &sc->sc_link[bus];
! 193: sc_link->adapter = &vs_scsiswitch;
! 194: sc_link->adapter_buswidth = 8;
! 195: sc_link->adapter_softc = sc;
! 196: sc_link->adapter_target = sc->sc_id[bus];
! 197: sc_link->device = &vs_scsidev;
! 198: #if 0
! 199: sc_link->luns = 1;
! 200: #endif
! 201: sc_link->openings = NUM_IOPB / 8;
! 202: if (bus != 0)
! 203: sc_link->flags = SDEV_2NDBUS;
! 204:
! 205: printf("%c%d", bus == 0 ? ' ' : '/', sc->sc_id[bus]);
! 206: }
! 207:
! 208: printf("\n");
! 209:
! 210: /*
! 211: * Attach all scsi units on us, watching for boot device
! 212: * (see device_register).
! 213: */
! 214: tmp = bootpart;
! 215: if (sc->sc_paddr != bootaddr)
! 216: bootpart = -1; /* invalid flag to device_register */
! 217:
! 218: for (bus = 0; bus < 2; bus++) {
! 219: if (sc->sc_id[bus] < 0)
! 220: continue;
! 221:
! 222: bzero(&saa, sizeof(saa));
! 223: saa.saa_sc_link = &sc->sc_link[bus];
! 224:
! 225: bootbus = bus;
! 226: config_found(self, &saa, scsiprint);
! 227: }
! 228:
! 229: bootpart = tmp; /* restore old values */
! 230: bootbus = 0;
! 231: }
! 232:
! 233: void
! 234: vs_print_addr(struct vs_softc *sc, struct scsi_xfer *xs)
! 235: {
! 236: if (xs == NULL)
! 237: printf("%s: ", sc->sc_dev.dv_xname);
! 238: else {
! 239: sc_print_addr(xs->sc_link);
! 240:
! 241: /* print bus number too if appropriate */
! 242: if (sc->sc_id[1] >= 0)
! 243: printf("(bus %d) ",
! 244: !!(xs->sc_link->flags & SDEV_2NDBUS));
! 245: }
! 246: }
! 247:
! 248: int
! 249: do_vspoll(struct vs_softc *sc, struct scsi_xfer *xs, int canreset)
! 250: {
! 251: int to;
! 252: int crsw, bus;
! 253:
! 254: if (xs != NULL) {
! 255: bus = !!(xs->sc_link->flags & SDEV_2NDBUS);
! 256: to = xs->timeout;
! 257: if (to == 0)
! 258: to = 2000;
! 259: } else {
! 260: bus = -1;
! 261: to = 2000;
! 262: }
! 263:
! 264: while (((crsw = CRSW) & (M_CRSW_CRBV | M_CRSW_CC)) == 0) {
! 265: if (to-- <= 0) {
! 266: vs_print_addr(sc, xs);
! 267: printf("command timeout, crsw 0x%x\n", crsw);
! 268:
! 269: if (canreset) {
! 270: vs_reset(sc, bus);
! 271: vs_resync(sc);
! 272: }
! 273: return 1;
! 274: }
! 275: delay(1000);
! 276: }
! 277: return 0;
! 278: }
! 279:
! 280: int
! 281: vs_poll(struct vs_softc *sc, struct vs_cb *cb)
! 282: {
! 283: struct scsi_xfer *xs;
! 284: int s;
! 285: int rc;
! 286:
! 287: xs = cb->cb_xs;
! 288: rc = do_vspoll(sc, xs, 1);
! 289:
! 290: s = splbio();
! 291: if (rc != 0) {
! 292: xs->error = XS_SELTIMEOUT;
! 293: xs->status = -1;
! 294: xs->flags |= ITSDONE;
! 295: #if 0
! 296: scsi_done(xs);
! 297: #endif
! 298: vs_free(cb);
! 299: } else
! 300: vs_scsidone(sc, cb);
! 301: splx(s);
! 302:
! 303: if (CRSW & M_CRSW_ER)
! 304: CRB_CLR_ER;
! 305: CRB_CLR_DONE;
! 306:
! 307: vs_clear_return_info(sc);
! 308: return (COMPLETE);
! 309: }
! 310:
! 311: void
! 312: thaw_queue(struct vs_softc *sc, int target)
! 313: {
! 314: THAW(target);
! 315:
! 316: /* loop until thawed */
! 317: while (THAW_REG & M_THAW_TWQE)
! 318: ;
! 319: }
! 320:
! 321: void
! 322: thaw_all_queues(struct vs_softc *sc)
! 323: {
! 324: int i;
! 325:
! 326: for (i = 1; i < NUM_WQ; i++)
! 327: thaw_queue(sc, i);
! 328: }
! 329:
! 330: void
! 331: vs_scsidone(struct vs_softc *sc, struct vs_cb *cb)
! 332: {
! 333: struct scsi_xfer *xs = cb->cb_xs;
! 334: u_int32_t len;
! 335: int error;
! 336:
! 337: len = vs_read(4, sh_RET_IOPB + IOPB_LENGTH);
! 338: xs->resid = xs->datalen - len;
! 339:
! 340: error = vs_read(2, sh_RET_IOPB + IOPB_STATUS);
! 341: if ((error & 0xff) == SCSI_SELECTION_TO) {
! 342: xs->error = XS_SELTIMEOUT;
! 343: xs->status = -1;
! 344: } else
! 345: xs->status = error >> 8;
! 346:
! 347: while (xs->status == SCSI_CHECK) {
! 348: vs_chksense(xs);
! 349: }
! 350:
! 351: xs->flags |= ITSDONE;
! 352: thaw_queue(sc, cb->cb_q);
! 353:
! 354: scsi_done(xs);
! 355:
! 356: vs_free(cb);
! 357: }
! 358:
! 359: int
! 360: vs_scsicmd(struct scsi_xfer *xs)
! 361: {
! 362: struct scsi_link *slp = xs->sc_link;
! 363: struct vs_softc *sc = slp->adapter_softc;
! 364: int flags, option;
! 365: unsigned int iopb_len;
! 366: bus_addr_t cqep, iopb;
! 367: struct vs_cb *cb;
! 368: u_int queue;
! 369: int s;
! 370:
! 371: flags = xs->flags;
! 372: if (flags & SCSI_POLL) {
! 373: cb = &sc->sc_cb[0];
! 374: cqep = sh_MCE;
! 375: iopb = sh_MCE_IOPB;
! 376:
! 377: #ifdef VS_DEBUG
! 378: if (mce_read(2, CQE_QECR) & M_QECR_GO)
! 379: printf("%s: master command queue busy\n",
! 380: sc->sc_dev.dv_xname);
! 381: #endif
! 382: /* Wait until we can use the command queue entry. */
! 383: while (mce_read(2, CQE_QECR) & M_QECR_GO)
! 384: ;
! 385: #ifdef VS_DEBUG
! 386: if (cb->cb_xs != NULL) {
! 387: printf("%s: master command not idle\n",
! 388: sc->sc_dev.dv_xname);
! 389: return (TRY_AGAIN_LATER);
! 390: }
! 391: #endif
! 392: s = splbio();
! 393: } else {
! 394: s = splbio();
! 395: cb = vs_find_queue(slp, sc);
! 396: if (cb == NULL) {
! 397: splx(s);
! 398: #ifdef VS_DEBUG
! 399: printf("%s: no free queues\n", sc->sc_dev.dv_xname);
! 400: #endif
! 401: return (TRY_AGAIN_LATER);
! 402: }
! 403: cqep = vs_getcqe(sc);
! 404: if (cqep == 0) {
! 405: splx(s);
! 406: return (TRY_AGAIN_LATER);
! 407: }
! 408: iopb = vs_getiopb(sc);
! 409: }
! 410:
! 411: queue = cb->cb_q;
! 412:
! 413: vs_bzero(iopb, IOPB_LONG_SIZE);
! 414:
! 415: /*
! 416: * We should only provide the iopb len if the controller is not
! 417: * able to compute it from the SCSI command group.
! 418: * Note that it has no knowledge of group 2.
! 419: */
! 420: switch ((xs->cmd->opcode) >> 5) {
! 421: case 0:
! 422: case 1:
! 423: case 5:
! 424: iopb_len = 0;
! 425: break;
! 426: default:
! 427: iopb_len = IOPB_SHORT_SIZE + xs->cmdlen;
! 428: break;
! 429: }
! 430:
! 431: bus_space_write_region_1(sc->sc_iot, sc->sc_ioh, iopb + IOPB_SCSI_DATA,
! 432: (u_int8_t *)xs->cmd, xs->cmdlen);
! 433:
! 434: vs_write(2, iopb + IOPB_CMD, IOPB_PASSTHROUGH);
! 435: vs_write(2, iopb + IOPB_UNIT,
! 436: IOPB_UNIT_VALUE(!!(slp->flags & SDEV_2NDBUS), slp->target, slp->lun));
! 437: vs_write(1, iopb + IOPB_NVCT, sc->sc_nvec);
! 438: vs_write(1, iopb + IOPB_EVCT, sc->sc_evec);
! 439:
! 440: /*
! 441: * Since the 88k doesn't support cache snooping, we have
! 442: * to flush the cache for a write and flush with inval for
! 443: * a read, prior to starting the IO.
! 444: */
! 445: dma_cachectl(pmap_kernel(), (vaddr_t)xs->data, xs->datalen,
! 446: flags & SCSI_DATA_IN ? DMA_CACHE_SYNC_INVAL : DMA_CACHE_SYNC);
! 447:
! 448: option = 0;
! 449: if (flags & SCSI_DATA_OUT)
! 450: option |= M_OPT_DIR;
! 451:
! 452: if (flags & SCSI_POLL) {
! 453: vs_write(2, iopb + IOPB_OPTION, option);
! 454: vs_write(2, iopb + IOPB_LEVEL, 0);
! 455: } else {
! 456: vs_write(2, iopb + IOPB_OPTION, option | M_OPT_IE);
! 457: vs_write(2, iopb + IOPB_LEVEL, sc->sc_ipl);
! 458: }
! 459: vs_write(2, iopb + IOPB_ADDR, ADDR_MOD);
! 460:
! 461: vs_write(2, cqep + CQE_IOPB_ADDR, iopb);
! 462: vs_write(1, cqep + CQE_IOPB_LENGTH, iopb_len);
! 463: vs_write(1, cqep + CQE_WORK_QUEUE, queue);
! 464:
! 465: cb->cb_xs = xs;
! 466: splx(s);
! 467:
! 468: if (xs->datalen != 0)
! 469: cb->cb_sg = vs_build_memory_structure(sc, xs, iopb);
! 470: else
! 471: cb->cb_sg = NULL;
! 472:
! 473: vs_write(4, cqep + CQE_CTAG, (u_int32_t)cb);
! 474:
! 475: if (crb_read(2, CRB_CRSW) & M_CRSW_AQ)
! 476: vs_write(2, cqep + CQE_QECR, M_QECR_AA | M_QECR_GO);
! 477: else
! 478: vs_write(2, cqep + CQE_QECR, M_QECR_GO);
! 479:
! 480: if (flags & SCSI_POLL) {
! 481: /* poll for the command to complete */
! 482: return vs_poll(sc, cb);
! 483: }
! 484:
! 485: return (SUCCESSFULLY_QUEUED);
! 486: }
! 487:
! 488: void
! 489: vs_chksense(struct scsi_xfer *xs)
! 490: {
! 491: int s;
! 492: struct scsi_link *slp = xs->sc_link;
! 493: struct vs_softc *sc = slp->adapter_softc;
! 494: struct scsi_sense *ss;
! 495:
! 496: /* ack and clear the error */
! 497: if (CRSW & M_CRSW_ER)
! 498: CRB_CLR_ER;
! 499: CRB_CLR_DONE;
! 500: xs->status = 0;
! 501:
! 502: /* Wait until we can use the command queue entry. */
! 503: while (mce_read(2, CQE_QECR) & M_QECR_GO)
! 504: ;
! 505:
! 506: vs_bzero(sh_MCE_IOPB, IOPB_LONG_SIZE);
! 507: /* This is a command, so point to it */
! 508: ss = (void *)(bus_space_vaddr(sc->sc_iot, sc->sc_ioh) +
! 509: sh_MCE_IOPB + IOPB_SCSI_DATA);
! 510: ss->opcode = REQUEST_SENSE;
! 511: ss->byte2 = slp->lun << 5;
! 512: ss->length = sizeof(struct scsi_sense_data);
! 513:
! 514: mce_iopb_write(2, IOPB_CMD, IOPB_PASSTHROUGH);
! 515: mce_iopb_write(2, IOPB_OPTION, 0);
! 516: mce_iopb_write(1, IOPB_NVCT, sc->sc_nvec);
! 517: mce_iopb_write(1, IOPB_EVCT, sc->sc_evec);
! 518: mce_iopb_write(2, IOPB_LEVEL, 0 /* sc->sc_ipl */);
! 519: mce_iopb_write(2, IOPB_ADDR, ADDR_MOD);
! 520: mce_iopb_write(4, IOPB_BUFF, kvtop((vaddr_t)&xs->sense));
! 521: mce_iopb_write(4, IOPB_LENGTH, sizeof(struct scsi_sense_data));
! 522: mce_iopb_write(2, IOPB_UNIT,
! 523: IOPB_UNIT_VALUE(!!(slp->flags & SDEV_2NDBUS), slp->target, slp->lun));
! 524:
! 525: vs_bzero(sh_MCE, CQE_SIZE);
! 526: mce_write(2, CQE_IOPB_ADDR, sh_MCE_IOPB);
! 527: mce_write(1, CQE_IOPB_LENGTH, 0);
! 528: mce_write(1, CQE_WORK_QUEUE, 0);
! 529: mce_write(2, CQE_QECR, M_QECR_GO);
! 530:
! 531: /* poll for the command to complete */
! 532: s = splbio();
! 533: do_vspoll(sc, xs, 1);
! 534: xs->status = vs_read(2, sh_RET_IOPB + IOPB_STATUS) >> 8;
! 535: splx(s);
! 536: }
! 537:
! 538: bus_addr_t
! 539: vs_getcqe(struct vs_softc *sc)
! 540: {
! 541: bus_addr_t cqep;
! 542: int qhdp;
! 543:
! 544: qhdp = mcsb_read(2, MCSB_QHDP);
! 545: cqep = sh_CQE(qhdp);
! 546:
! 547: if (vs_read(2, cqep + CQE_QECR) & M_QECR_GO) {
! 548: /* should never happen */
! 549: return 0;
! 550: }
! 551:
! 552: if (++qhdp == NUM_CQE)
! 553: qhdp = 0;
! 554: mcsb_write(2, MCSB_QHDP, qhdp);
! 555:
! 556: vs_bzero(cqep, CQE_SIZE);
! 557: return cqep;
! 558: }
! 559:
! 560: bus_addr_t
! 561: vs_getiopb(struct vs_softc *sc)
! 562: {
! 563: bus_addr_t iopb;
! 564: int qhdp;
! 565:
! 566: /*
! 567: * Since we are always invoked after vs_getcqe(), qhdp has already
! 568: * been incremented...
! 569: */
! 570: qhdp = mcsb_read(2, MCSB_QHDP);
! 571: if (--qhdp < 0)
! 572: qhdp = NUM_CQE - 1;
! 573:
! 574: iopb = sh_IOPB(qhdp);
! 575: return iopb;
! 576: }
! 577:
! 578: int
! 579: vs_initialize(struct vs_softc *sc)
! 580: {
! 581: int i, msr, dbid;
! 582:
! 583: for (i = 0; i < NUM_WQ; i++)
! 584: sc->sc_cb[i].cb_q = i;
! 585:
! 586: /*
! 587: * Reset the board, and wait for it to get ready.
! 588: * The reset signal is applied for 70 usec, and the board status
! 589: * is not tested until 100 usec after the reset signal has been
! 590: * cleared, per the manual (MVME328/D1) pages 4-6 and 4-9.
! 591: */
! 592:
! 593: mcsb_write(2, MCSB_MCR, M_MCR_RES | M_MCR_SFEN);
! 594: delay(70);
! 595: mcsb_write(2, MCSB_MCR, M_MCR_SFEN);
! 596:
! 597: delay(100);
! 598: i = 0;
! 599: for (;;) {
! 600: msr = mcsb_read(2, MCSB_MSR);
! 601: if ((msr & (M_MSR_BOK | M_MSR_CNA)) == M_MSR_BOK)
! 602: break;
! 603: if (++i > 5000) {
! 604: printf("board reset failed, status %x\n", msr);
! 605: return 1;
! 606: }
! 607: delay(1000);
! 608: }
! 609:
! 610: /* initialize channels id */
! 611: sc->sc_id[0] = csb_read(1, CSB_PID);
! 612: sc->sc_id[1] = -1;
! 613: switch (dbid = csb_read(1, CSB_DBID)) {
! 614: case DBID_SCSI2:
! 615: case DBID_SCSI:
! 616: #if 0
! 617: printf("daughter board, ");
! 618: #endif
! 619: sc->sc_id[1] = csb_read(1, CSB_SID);
! 620: break;
! 621: case DBID_PRINTER:
! 622: printf("printer port, ");
! 623: break;
! 624: case DBID_NONE:
! 625: break;
! 626: default:
! 627: printf("unknown daughterboard id %x, ", dbid);
! 628: break;
! 629: }
! 630:
! 631: CRB_CLR_DONE;
! 632: mcsb_write(2, MCSB_QHDP, 0);
! 633:
! 634: vs_bzero(sh_CIB, CIB_SIZE);
! 635: cib_write(2, CIB_NCQE, NUM_CQE);
! 636: cib_write(2, CIB_BURST, 0);
! 637: cib_write(2, CIB_NVECT, (sc->sc_ipl << 8) | sc->sc_nvec);
! 638: cib_write(2, CIB_EVECT, (sc->sc_ipl << 8) | sc->sc_evec);
! 639: cib_write(2, CIB_PID, 0x08); /* XXX default */
! 640: cib_write(2, CIB_SID, 0x08);
! 641: cib_write(2, CIB_CRBO, sh_CRB);
! 642: cib_write(4, CIB_SELECT, SELECTION_TIMEOUT);
! 643: cib_write(4, CIB_WQTIMO, 4);
! 644: cib_write(4, CIB_VMETIMO, 0 /* VME_BUS_TIMEOUT */);
! 645: cib_write(2, CIB_ERR_FLGS, M_ERRFLGS_RIN | M_ERRFLGS_RSE);
! 646: cib_write(2, CIB_SBRIV, (sc->sc_ipl << 8) | sc->sc_evec);
! 647: cib_write(1, CIB_SOF0, 0x15);
! 648: cib_write(1, CIB_SRATE0, 100 / 4);
! 649: cib_write(1, CIB_SOF1, 0);
! 650: cib_write(1, CIB_SRATE1, 0);
! 651:
! 652: vs_bzero(sh_MCE_IOPB, IOPB_LONG_SIZE);
! 653: mce_iopb_write(2, IOPB_CMD, CNTR_INIT);
! 654: mce_iopb_write(2, IOPB_OPTION, 0);
! 655: mce_iopb_write(1, IOPB_NVCT, sc->sc_nvec);
! 656: mce_iopb_write(1, IOPB_EVCT, sc->sc_evec);
! 657: mce_iopb_write(2, IOPB_LEVEL, 0 /* sc->sc_ipl */);
! 658: mce_iopb_write(2, IOPB_ADDR, SHIO_MOD);
! 659: mce_iopb_write(4, IOPB_BUFF, sh_CIB);
! 660: mce_iopb_write(4, IOPB_LENGTH, CIB_SIZE);
! 661:
! 662: vs_bzero(sh_MCE, CQE_SIZE);
! 663: mce_write(2, CQE_IOPB_ADDR, sh_MCE_IOPB);
! 664: mce_write(1, CQE_IOPB_LENGTH, 0);
! 665: mce_write(1, CQE_WORK_QUEUE, 0);
! 666: mce_write(2, CQE_QECR, M_QECR_GO);
! 667:
! 668: /* poll for the command to complete */
! 669: do_vspoll(sc, NULL, 1);
! 670:
! 671: /* initialize work queues */
! 672: for (i = 1; i < NUM_WQ; i++) {
! 673: /* Wait until we can use the command queue entry. */
! 674: while (mce_read(2, CQE_QECR) & M_QECR_GO)
! 675: ;
! 676:
! 677: vs_bzero(sh_MCE_IOPB, IOPB_LONG_SIZE);
! 678: mce_iopb_write(2, WQCF_CMD, CNTR_INIT_WORKQ);
! 679: mce_iopb_write(2, WQCF_OPTION, 0);
! 680: mce_iopb_write(1, WQCF_NVCT, sc->sc_nvec);
! 681: mce_iopb_write(1, WQCF_EVCT, sc->sc_evec);
! 682: mce_iopb_write(2, WQCF_ILVL, 0 /* sc->sc_ipl */);
! 683: mce_iopb_write(2, WQCF_WORKQ, i);
! 684: mce_iopb_write(2, WQCF_WOPT, M_WOPT_FE | M_WOPT_IWQ);
! 685: mce_iopb_write(2, WQCF_SLOTS, JAGUAR_MAX_Q_SIZ);
! 686: mce_iopb_write(4, WQCF_CMDTO, 4); /* 1 second */
! 687:
! 688: vs_bzero(sh_MCE, CQE_SIZE);
! 689: mce_write(2, CQE_IOPB_ADDR, sh_MCE_IOPB);
! 690: mce_write(1, CQE_IOPB_LENGTH, 0);
! 691: mce_write(1, CQE_WORK_QUEUE, 0);
! 692: mce_write(2, CQE_QECR, M_QECR_GO);
! 693:
! 694: /* poll for the command to complete */
! 695: do_vspoll(sc, NULL, 1);
! 696: if (CRSW & M_CRSW_ER) {
! 697: printf("work queue %d initialization error 0x%x\n",
! 698: i, vs_read(2, sh_RET_IOPB + IOPB_STATUS));
! 699: return 1;
! 700: }
! 701: CRB_CLR_DONE;
! 702: }
! 703:
! 704: /* start queue mode */
! 705: mcsb_write(2, MCSB_MCR, mcsb_read(2, MCSB_MCR) | M_MCR_SQM);
! 706:
! 707: /* reset all SCSI buses */
! 708: vs_reset(sc, -1);
! 709: /* sync all devices */
! 710: vs_resync(sc);
! 711:
! 712: return 0;
! 713: }
! 714:
! 715: void
! 716: vs_resync(struct vs_softc *sc)
! 717: {
! 718: int bus, target;
! 719:
! 720: for (bus = 0; bus < 2; bus++) {
! 721: if (sc->sc_id[bus] < 0)
! 722: break;
! 723:
! 724: for (target = 0; target < 8; target++) {
! 725: if (target == sc->sc_id[bus])
! 726: continue;
! 727:
! 728: /* Wait until we can use the command queue entry. */
! 729: while (mce_read(2, CQE_QECR) & M_QECR_GO)
! 730: ;
! 731:
! 732: vs_bzero(sh_MCE_IOPB, IOPB_SHORT_SIZE);
! 733: mce_iopb_write(2, DRCF_CMD, CNTR_DEV_REINIT);
! 734: mce_iopb_write(2, DRCF_OPTION, 0); /* prefer polling */
! 735: mce_iopb_write(1, DRCF_NVCT, sc->sc_nvec);
! 736: mce_iopb_write(1, DRCF_EVCT, sc->sc_evec);
! 737: mce_iopb_write(2, DRCF_ILVL, 0);
! 738: mce_iopb_write(2, DRCF_UNIT,
! 739: IOPB_UNIT_VALUE(bus, target, 0));
! 740:
! 741: vs_bzero(sh_MCE, CQE_SIZE);
! 742: mce_write(2, CQE_IOPB_ADDR, sh_MCE_IOPB);
! 743: mce_write(1, CQE_IOPB_LENGTH, 0);
! 744: mce_write(1, CQE_WORK_QUEUE, 0);
! 745: mce_write(2, CQE_QECR, M_QECR_GO);
! 746:
! 747: /* poll for the command to complete */
! 748: do_vspoll(sc, NULL, 0);
! 749: if (CRSW & M_CRSW_ER)
! 750: CRB_CLR_ER;
! 751: CRB_CLR_DONE;
! 752: }
! 753: }
! 754: }
! 755:
! 756: void
! 757: vs_reset(struct vs_softc *sc, int bus)
! 758: {
! 759: int b, s;
! 760:
! 761: s = splbio();
! 762:
! 763: for (b = 0; b < 2; b++) {
! 764: if (bus >= 0 && b != bus)
! 765: continue;
! 766:
! 767: /* Wait until we can use the command queue entry. */
! 768: while (mce_read(2, CQE_QECR) & M_QECR_GO)
! 769: ;
! 770:
! 771: vs_bzero(sh_MCE_IOPB, IOPB_SHORT_SIZE);
! 772: mce_iopb_write(2, SRCF_CMD, IOPB_RESET);
! 773: mce_iopb_write(2, SRCF_OPTION, 0); /* prefer polling */
! 774: mce_iopb_write(1, SRCF_NVCT, sc->sc_nvec);
! 775: mce_iopb_write(1, SRCF_EVCT, sc->sc_evec);
! 776: mce_iopb_write(2, SRCF_ILVL, 0);
! 777: mce_iopb_write(2, SRCF_BUSID, b << 15);
! 778:
! 779: vs_bzero(sh_MCE, CQE_SIZE);
! 780: mce_write(2, CQE_IOPB_ADDR, sh_MCE_IOPB);
! 781: mce_write(1, CQE_IOPB_LENGTH, 0);
! 782: mce_write(1, CQE_WORK_QUEUE, 0);
! 783: mce_write(2, CQE_QECR, M_QECR_GO);
! 784:
! 785: /* poll for the command to complete */
! 786: for (;;) {
! 787: do_vspoll(sc, NULL, 0);
! 788: /* ack & clear scsi error condition cause by reset */
! 789: if (CRSW & M_CRSW_ER) {
! 790: CRB_CLR_DONE;
! 791: vs_write(2, sh_RET_IOPB + IOPB_STATUS, 0);
! 792: break;
! 793: }
! 794: CRB_CLR_DONE;
! 795: }
! 796: }
! 797:
! 798: thaw_all_queues(sc);
! 799:
! 800: splx(s);
! 801: }
! 802:
! 803: /* free a cb; invoked at splbio */
! 804: static __inline__ void
! 805: vs_free(struct vs_cb *cb)
! 806: {
! 807: if (cb->cb_sg != NULL) {
! 808: vs_dealloc_scatter_gather(cb->cb_sg);
! 809: cb->cb_sg = NULL;
! 810: }
! 811: cb->cb_xs = NULL;
! 812: }
! 813:
! 814: /* normal interrupt routine */
! 815: int
! 816: vs_nintr(void *vsc)
! 817: {
! 818: struct vs_softc *sc = (struct vs_softc *)vsc;
! 819: struct vs_cb *cb;
! 820: int s;
! 821:
! 822: if ((CRSW & CONTROLLER_ERROR) == CONTROLLER_ERROR)
! 823: return vs_eintr(sc);
! 824:
! 825: /* Got a valid interrupt on this device */
! 826: s = splbio();
! 827: cb = (struct vs_cb *)crb_read(4, CRB_CTAG);
! 828:
! 829: /*
! 830: * If this is a controller error, there won't be a cb
! 831: * pointer in the CTAG field. Bad things happen if you try
! 832: * to point to address 0. But then, we should have caught
! 833: * the controller error above.
! 834: */
! 835: if (cb != NULL)
! 836: vs_scsidone(sc, cb);
! 837:
! 838: /* ack the interrupt */
! 839: if (CRSW & M_CRSW_ER)
! 840: CRB_CLR_ER;
! 841: CRB_CLR_DONE;
! 842:
! 843: vs_clear_return_info(sc);
! 844: splx(s);
! 845:
! 846: return 1;
! 847: }
! 848:
! 849: /* error interrupts */
! 850: int
! 851: vs_eintr(void *vsc)
! 852: {
! 853: struct vs_softc *sc = (struct vs_softc *)vsc;
! 854: struct vs_cb *cb;
! 855: struct scsi_xfer *xs;
! 856: int crsw, ecode;
! 857: int s;
! 858:
! 859: /* Got a valid interrupt on this device */
! 860: s = splbio();
! 861:
! 862: crsw = vs_read(2, sh_CEVSB + CEVSB_CRSW);
! 863: ecode = vs_read(1, sh_CEVSB + CEVSB_ERROR);
! 864: cb = (struct vs_cb *)crb_read(4, CRB_CTAG);
! 865: xs = cb != NULL ? cb->cb_xs : NULL;
! 866:
! 867: vs_print_addr(sc, xs);
! 868:
! 869: if (crsw & M_CRSW_RST) {
! 870: printf("bus reset\n");
! 871: } else {
! 872: switch (ecode) {
! 873: case CEVSB_ERR_TYPE:
! 874: printf("IOPB type error\n");
! 875: break;
! 876: case CEVSB_ERR_TO:
! 877: printf("timeout\n");
! 878: break;
! 879: case CEVSB_ERR_TR:
! 880: printf("reconnect error\n");
! 881: break;
! 882: case CEVSB_ERR_OF:
! 883: printf("overflow\n");
! 884: break;
! 885: case CEVSB_ERR_BD:
! 886: printf("bad direction\n");
! 887: break;
! 888: case CEVSB_ERR_NR:
! 889: printf("non-recoverable error\n");
! 890: break;
! 891: case CEVSB_ERR_PANIC:
! 892: printf("board panic\n");
! 893: break;
! 894: default:
! 895: printf("unexpected error %x\n", ecode);
! 896: break;
! 897: }
! 898: }
! 899:
! 900: if (xs != NULL) {
! 901: xs->error = XS_SELTIMEOUT;
! 902: xs->status = -1;
! 903: xs->flags |= ITSDONE;
! 904: scsi_done(xs);
! 905: }
! 906:
! 907: if (CRSW & M_CRSW_ER)
! 908: CRB_CLR_ER;
! 909: CRB_CLR_DONE;
! 910:
! 911: thaw_all_queues(sc);
! 912: vs_clear_return_info(sc);
! 913: splx(s);
! 914:
! 915: return 1;
! 916: }
! 917:
! 918: static void
! 919: vs_clear_return_info(struct vs_softc *sc)
! 920: {
! 921: vs_bzero(sh_RET_IOPB, CRB_SIZE + IOPB_LONG_SIZE);
! 922: }
! 923:
! 924: /*
! 925: * Choose the first available work queue (invoked at splbio).
! 926: * We used a simple round-robin mechanism which is faster than rescanning
! 927: * from the beginning if we have more than one target on the bus.
! 928: */
! 929: struct vs_cb *
! 930: vs_find_queue(struct scsi_link *sl, struct vs_softc *sc)
! 931: {
! 932: struct vs_cb *cb;
! 933: static u_int last = 0;
! 934: u_int q;
! 935:
! 936: q = last;
! 937: for (;;) {
! 938: if (++q == NUM_WQ)
! 939: q = 1;
! 940: if (q == last)
! 941: break;
! 942:
! 943: if ((cb = &sc->sc_cb[q])->cb_xs == NULL) {
! 944: last = q;
! 945: return (cb);
! 946: }
! 947: }
! 948:
! 949: return (NULL);
! 950: }
! 951:
! 952: /*
! 953: * Useful functions for scatter/gather list
! 954: */
! 955:
! 956: M328_SG
! 957: vs_alloc_scatter_gather(void)
! 958: {
! 959: M328_SG sg;
! 960:
! 961: MALLOC(sg, M328_SG, sizeof(struct m328_sg), M_DEVBUF, M_WAITOK);
! 962: bzero(sg, sizeof(struct m328_sg));
! 963:
! 964: return (sg);
! 965: }
! 966:
! 967: void
! 968: vs_dealloc_scatter_gather(M328_SG sg)
! 969: {
! 970: int i;
! 971:
! 972: if (sg->level > 0) {
! 973: for (i = 0; sg->down[i] && i < MAX_SG_ELEMENTS; i++) {
! 974: vs_dealloc_scatter_gather(sg->down[i]);
! 975: }
! 976: }
! 977: FREE(sg, M_DEVBUF);
! 978: }
! 979:
! 980: void
! 981: vs_link_sg_element(sg_list_element_t *element, vaddr_t phys_add, int len)
! 982: {
! 983: element->count.bytes = len;
! 984: element->addrlo = phys_add;
! 985: element->addrhi = phys_add >> 16;
! 986: element->link = 0; /* FALSE */
! 987: element->transfer_type = NORMAL_TYPE;
! 988: element->memory_type = LONG_TRANSFER;
! 989: element->address_modifier = ADRM_EXT_S_D;
! 990: }
! 991:
! 992: void
! 993: vs_link_sg_list(sg_list_element_t *list, vaddr_t phys_add, int elements)
! 994: {
! 995: list->count.scatter.gather = elements;
! 996: list->addrlo = phys_add;
! 997: list->addrhi = phys_add >> 16;
! 998: list->link = 1; /* TRUE */
! 999: list->transfer_type = NORMAL_TYPE;
! 1000: list->memory_type = LONG_TRANSFER;
! 1001: list->address_modifier = ADRM_EXT_S_D;
! 1002: }
! 1003:
! 1004: M328_SG
! 1005: vs_build_memory_structure(struct vs_softc *sc, struct scsi_xfer *xs,
! 1006: bus_addr_t iopb)
! 1007: {
! 1008: M328_SG sg;
! 1009: vaddr_t starting_point_virt, starting_point_phys, point_virt,
! 1010: point1_phys, point2_phys, virt;
! 1011: unsigned int len;
! 1012: int level;
! 1013:
! 1014: sg = NULL; /* Hopefully we need no scatter/gather list */
! 1015:
! 1016: /*
! 1017: * We have the following things:
! 1018: * virt va of the virtual memory block
! 1019: * len length of the virtual memory block
! 1020: * starting_point_virt va of the physical memory block
! 1021: * starting_point_phys pa of the physical memory block
! 1022: * point_virt va of the virtual memory
! 1023: * we are checking at the moment
! 1024: * point1_phys pa of the physical memory
! 1025: * we are checking at the moment
! 1026: * point2_phys pa of another physical memory
! 1027: * we are checking at the moment
! 1028: */
! 1029:
! 1030: level = 0;
! 1031: virt = starting_point_virt = (vaddr_t)xs->data;
! 1032: point1_phys = starting_point_phys = kvtop((vaddr_t)xs->data);
! 1033: len = xs->datalen;
! 1034:
! 1035: /*
! 1036: * Check if we need scatter/gather
! 1037: */
! 1038: if (trunc_page(virt + len - 1) != trunc_page(virt)) {
! 1039: for (point_virt = round_page(starting_point_virt + 1);
! 1040: /* if we do already scatter/gather we have to stay in the loop and jump */
! 1041: point_virt < virt + len || sg != NULL;
! 1042: point_virt += PAGE_SIZE) { /* out later */
! 1043:
! 1044: point2_phys = kvtop(point_virt);
! 1045:
! 1046: if ((point2_phys != trunc_page(point1_phys) + PAGE_SIZE) || /* physical memory is not contiguous */
! 1047: (point_virt - starting_point_virt >= MAX_SG_BLOCK_SIZE && sg)) { /* we only can access (1<<16)-1 bytes in scatter/gather_mode */
! 1048: if (point_virt - starting_point_virt >= MAX_SG_BLOCK_SIZE) { /* We were walking too far for one scatter/gather block ... */
! 1049: point_virt = trunc_page(starting_point_virt+MAX_SG_BLOCK_SIZE-1); /* So go back to the beginning of the last matching page */
! 1050: /* and generate the physical address of
! 1051: * this location for the next time. */
! 1052: point2_phys = kvtop(point_virt);
! 1053: }
! 1054:
! 1055: if (sg == NULL)
! 1056: sg = vs_alloc_scatter_gather();
! 1057:
! 1058: #if 1 /* broken firmware */
! 1059: if (sg->elements >= MAX_SG_ELEMENTS) {
! 1060: vs_dealloc_scatter_gather(sg);
! 1061: printf("%s: scatter/gather list too large\n",
! 1062: sc->sc_dev.dv_xname);
! 1063: return (NULL);
! 1064: }
! 1065: #else /* if the firmware will ever get fixed */
! 1066: while (sg->elements >= MAX_SG_ELEMENTS) {
! 1067: if (!sg->up) { /* If the list full in this layer ? */
! 1068: sg->up = vs_alloc_scatter_gather();
! 1069: sg->up->level = sg->level+1;
! 1070: sg->up->down[0] = sg;
! 1071: sg->up->elements = 1;
! 1072: }
! 1073: /* link this full list also in physical memory */
! 1074: vs_link_sg_list(&(sg->up->list[sg->up->elements-1]),
! 1075: kvtop((vaddr_t)sg->list),
! 1076: sg->elements);
! 1077: sg = sg->up; /* Climb up */
! 1078: }
! 1079: while (sg->level) { /* As long as we are not a the base level */
! 1080: int i;
! 1081:
! 1082: i = sg->elements;
! 1083: /* We need a new element */
! 1084: sg->down[i] = vs_alloc_scatter_gather();
! 1085: sg->down[i]->level = sg->level - 1;
! 1086: sg->down[i]->up = sg;
! 1087: sg->elements++;
! 1088: sg = sg->down[i]; /* Climb down */
! 1089: }
! 1090: #endif /* 1 */
! 1091: if (point_virt < virt + len) {
! 1092: /* linking element */
! 1093: vs_link_sg_element(&(sg->list[sg->elements]),
! 1094: starting_point_phys,
! 1095: point_virt - starting_point_virt);
! 1096: sg->elements++;
! 1097: } else {
! 1098: /* linking last element */
! 1099: vs_link_sg_element(&(sg->list[sg->elements]),
! 1100: starting_point_phys,
! 1101: virt + len - starting_point_virt);
! 1102: sg->elements++;
! 1103: break; /* We have now collected all blocks */
! 1104: }
! 1105: starting_point_virt = point_virt;
! 1106: starting_point_phys = point2_phys;
! 1107: }
! 1108: point1_phys = point2_phys;
! 1109: }
! 1110: }
! 1111:
! 1112: /*
! 1113: * Climb up along the right side of the tree until we reach the top.
! 1114: */
! 1115:
! 1116: if (sg != NULL) {
! 1117: while (sg->up) {
! 1118: /* link this list also in physical memory */
! 1119: vs_link_sg_list(&(sg->up->list[sg->up->elements-1]),
! 1120: kvtop((vaddr_t)sg->list),
! 1121: sg->elements);
! 1122: sg = sg->up; /* Climb up */
! 1123: }
! 1124:
! 1125: vs_write(2, iopb + IOPB_OPTION,
! 1126: vs_read(2, iopb + IOPB_OPTION) | M_OPT_SG);
! 1127: vs_write(2, iopb + IOPB_ADDR,
! 1128: vs_read(2, iopb + IOPB_ADDR) | M_ADR_SG_LINK);
! 1129: vs_write(4, iopb + IOPB_BUFF, kvtop((vaddr_t)sg->list));
! 1130: vs_write(4, iopb + IOPB_LENGTH, sg->elements);
! 1131: vs_write(4, iopb + IOPB_SGTTL, len);
! 1132: } else {
! 1133: /* no scatter/gather necessary */
! 1134: vs_write(4, iopb + IOPB_BUFF, starting_point_phys);
! 1135: vs_write(4, iopb + IOPB_LENGTH, len);
! 1136: }
! 1137: return sg;
! 1138: }
! 1139:
! 1140: static paddr_t
! 1141: kvtop(vaddr_t va)
! 1142: {
! 1143: paddr_t pa;
! 1144:
! 1145: pmap_extract(pmap_kernel(), va, &pa);
! 1146: /* XXX check for failure */
! 1147: return pa;
! 1148: }
CVSweb