Annotation of sys/arch/sparc/dev/isp_sbus.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: isp_sbus.c,v 1.24 2004/09/29 07:35:11 miod Exp $ */
! 2: /*
! 3: * SBus specific probe and attach routines for Qlogic ISP SCSI adapters.
! 4: *
! 5: * Copyright (c) 1997, 2000 by Matthew Jacob
! 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 immediately at the beginning of the file, without modification,
! 13: * this list of conditions, and the following disclaimer.
! 14: * documentation and/or other materials provided with the distribution.
! 15: * 2. The name of the author may not be used to endorse or promote products
! 16: * derived from this software without specific prior written permission.
! 17: *
! 18: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
! 19: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 20: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 21: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
! 22: * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 23: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 24: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 25: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 26: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 27: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 28: * SUCH DAMAGE.
! 29: *
! 30: */
! 31:
! 32: #include <sys/param.h>
! 33: #include <sys/systm.h>
! 34: #include <sys/device.h>
! 35: #include <sys/kernel.h>
! 36: #include <sys/malloc.h>
! 37: #include <sys/queue.h>
! 38:
! 39: #include <machine/autoconf.h>
! 40: #include <machine/cpu.h>
! 41: #include <machine/param.h>
! 42: #include <machine/vmparam.h>
! 43: #include <sparc/sparc/cpuvar.h>
! 44:
! 45: #include <dev/ic/isp_openbsd.h>
! 46: #if defined(ISP_COMPILE_FW) || defined(ISP_COMPILE_1000_FW)
! 47: #include <dev/microcode/isp/asm_sbus.h>
! 48: #endif
! 49:
! 50: #define ISP_SBUSIFY_ISPHDR(isp, hdrp) \
! 51: ISP_SWAP8((hdrp)->rqs_entry_count, (hdrp)->rqs_entry_type); \
! 52: ISP_SWAP8((hdrp)->rqs_flags, (hdrp)->rqs_seqno);
! 53:
! 54: #define ISP_SWIZZLE_REQUEST(a, b) \
! 55: ISP_SBUSIFY_ISPHDR(a, &(b)->req_header); \
! 56: ISP_SWAP8((b)->req_target, (b)->req_lun_trn)
! 57:
! 58:
! 59: static int
! 60: isp_sbus_rd_isr(struct ispsoftc *, u_int16_t *, u_int16_t *, u_int16_t *);
! 61: static u_int16_t isp_sbus_rd_reg(struct ispsoftc *, int);
! 62: static void isp_sbus_wr_reg(struct ispsoftc *, int, u_int16_t);
! 63: static int isp_sbus_mbxdma(struct ispsoftc *);
! 64: static int isp_sbus_dmasetup(struct ispsoftc *, struct scsi_xfer *,
! 65: ispreq_t *, u_int16_t *, u_int16_t);
! 66: static void
! 67: isp_sbus_dmateardown(struct ispsoftc *, struct scsi_xfer *, u_int16_t);
! 68: static int isp_sbus_intr(void *);
! 69:
! 70: #ifndef ISP_1000_RISC_CODE
! 71: #define ISP_1000_RISC_CODE NULL
! 72: #endif
! 73:
! 74: static struct ispmdvec mdvec = {
! 75: isp_sbus_rd_isr,
! 76: isp_sbus_rd_reg,
! 77: isp_sbus_wr_reg,
! 78: isp_sbus_mbxdma,
! 79: isp_sbus_dmasetup,
! 80: isp_sbus_dmateardown,
! 81: NULL,
! 82: NULL,
! 83: NULL,
! 84: (u_int16_t *) ISP_1000_RISC_CODE,
! 85: BIU_BURST_ENABLE|BIU_SBUS_CONF1_FIFO_32
! 86: };
! 87:
! 88: struct isp_sbussoftc {
! 89: struct ispsoftc sbus_isp;
! 90: sdparam sbus_dev;
! 91: struct intrhand sbus_ih;
! 92: volatile u_int16_t *sbus_reg;
! 93: int sbus_node;
! 94: int sbus_pri;
! 95: struct ispmdvec sbus_mdvec;
! 96: int16_t sbus_poff[_NREG_BLKS];
! 97: vaddr_t *sbus_kdma_allocs;
! 98: };
! 99:
! 100:
! 101: static int isp_match(struct device *, void *, void *);
! 102: static void isp_sbus_attach(struct device *, struct device *, void *);
! 103: struct cfattach isp_sbus_ca = {
! 104: sizeof (struct isp_sbussoftc), isp_match, isp_sbus_attach
! 105: };
! 106:
! 107: static int
! 108: isp_match(struct device *parent, void *cfarg, void *aux)
! 109: {
! 110: int rv;
! 111: struct cfdata *cf = cfarg;
! 112: #ifdef DEBUG
! 113: static int oneshot = 1;
! 114: #endif
! 115: struct confargs *ca = aux;
! 116: struct romaux *ra = &ca->ca_ra;
! 117:
! 118: rv = (strcmp(cf->cf_driver->cd_name, ra->ra_name) == 0 ||
! 119: strcmp("PTI,ptisp", ra->ra_name) == 0 ||
! 120: strcmp("ptisp", ra->ra_name) == 0 ||
! 121: strcmp("SUNW,isp", ra->ra_name) == 0 ||
! 122: strcmp("QLGC,isp", ra->ra_name) == 0);
! 123: if (rv == 0)
! 124: return (rv);
! 125: #ifdef DEBUG
! 126: if (rv && oneshot) {
! 127: oneshot = 0;
! 128: printf("Qlogic ISP Driver, OpenBSD (sbus) Platform Version "
! 129: "%d.%d Core Version %d.%d\n",
! 130: ISP_PLATFORM_VERSION_MAJOR, ISP_PLATFORM_VERSION_MINOR,
! 131: ISP_CORE_VERSION_MAJOR, ISP_CORE_VERSION_MINOR);
! 132: }
! 133: #endif
! 134: if (ca->ca_bustype == BUS_SBUS)
! 135: return (1);
! 136: ra->ra_len = NBPG;
! 137: return (probeget(ra->ra_vaddr, 1) != -1);
! 138: }
! 139:
! 140: static void
! 141: isp_sbus_attach(struct device *parent, struct device *self, void *aux)
! 142: {
! 143: int freq, storebp = 0;
! 144: struct confargs *ca = aux;
! 145: struct bootpath *bp;
! 146: struct isp_sbussoftc *sbc = (struct isp_sbussoftc *) self;
! 147: struct ispsoftc *isp = &sbc->sbus_isp;
! 148:
! 149: if (ca->ca_ra.ra_nintr != 1) {
! 150: printf(": expected 1 interrupt, got %d\n", ca->ca_ra.ra_nintr);
! 151: return;
! 152: }
! 153:
! 154: printf("\n");
! 155:
! 156: sbc->sbus_pri = ca->ca_ra.ra_intr[0].int_pri;
! 157: sbc->sbus_mdvec = mdvec;
! 158:
! 159: if (ca->ca_ra.ra_vaddr) {
! 160: sbc->sbus_reg = (volatile u_int16_t *) ca->ca_ra.ra_vaddr;
! 161: } else {
! 162: sbc->sbus_reg = (volatile u_int16_t *)
! 163: mapiodev(ca->ca_ra.ra_reg, 0, ca->ca_ra.ra_len);
! 164: }
! 165: sbc->sbus_node = ca->ca_ra.ra_node;
! 166:
! 167: freq = getpropint(ca->ca_ra.ra_node, "clock-frequency", 0);
! 168: if (freq) {
! 169: /*
! 170: * Convert from HZ to MHz, rounding up.
! 171: */
! 172: freq = (freq + 500000)/1000000;
! 173: #if 0
! 174: printf("%s: %d MHz\n", self->dv_xname, freq);
! 175: #endif
! 176: }
! 177: sbc->sbus_mdvec.dv_clock = freq;
! 178:
! 179: if ((bp = ca->ca_ra.ra_bp) != NULL) {
! 180: if (bp->val[0] == ca->ca_slot &&
! 181: bp->val[1] == ca->ca_offset) {
! 182: if (strcmp("isp", bp->name) == 0 ||
! 183: strcmp("QLGC,isp", bp->name) == 0 ||
! 184: strcmp("PTI,isp", bp->name) == 0 ||
! 185: strcmp("ptisp", bp->name) == 0) {
! 186: storebp = 1;
! 187: }
! 188: }
! 189: }
! 190:
! 191: /*
! 192: * XXX: Now figure out what the proper burst sizes, etc., to use.
! 193: */
! 194: sbc->sbus_mdvec.dv_conf1 |= BIU_SBUS_CONF1_FIFO_8;
! 195:
! 196: /*
! 197: * Some early versions of the PTI SBus adapter
! 198: * would fail in trying to download (via poking)
! 199: * FW. We give up on them.
! 200: */
! 201: if (strcmp("PTI,ptisp", ca->ca_ra.ra_name) == 0 ||
! 202: strcmp("ptisp", ca->ca_ra.ra_name) == 0) {
! 203: sbc->sbus_mdvec.dv_ispfw = NULL;
! 204: }
! 205:
! 206: isp->isp_mdvec = &sbc->sbus_mdvec;
! 207: isp->isp_bustype = ISP_BT_SBUS;
! 208: isp->isp_type = ISP_HA_SCSI_UNKNOWN;
! 209: isp->isp_param = &sbc->sbus_dev;
! 210: bzero(isp->isp_param, sizeof (sdparam));
! 211:
! 212: sbc->sbus_poff[BIU_BLOCK >> _BLK_REG_SHFT] = BIU_REGS_OFF;
! 213: sbc->sbus_poff[MBOX_BLOCK >> _BLK_REG_SHFT] = SBUS_MBOX_REGS_OFF;
! 214: sbc->sbus_poff[SXP_BLOCK >> _BLK_REG_SHFT] = SBUS_SXP_REGS_OFF;
! 215: sbc->sbus_poff[RISC_BLOCK >> _BLK_REG_SHFT] = SBUS_RISC_REGS_OFF;
! 216: sbc->sbus_poff[DMA_BLOCK >> _BLK_REG_SHFT] = DMA_REGS_OFF;
! 217:
! 218: /* Establish interrupt channel */
! 219: sbc->sbus_ih.ih_fun = (void *) isp_sbus_intr;
! 220: sbc->sbus_ih.ih_arg = sbc;
! 221: intr_establish(sbc->sbus_pri, &sbc->sbus_ih, IPL_BIO, self->dv_xname);
! 222:
! 223: /*
! 224: * Set up logging levels.
! 225: */
! 226: #ifdef ISP_LOGDEFAULT
! 227: isp->isp_dblev = ISP_LOGDEFAULT;
! 228: #else
! 229: isp->isp_dblev = ISP_LOGWARN|ISP_LOGERR;
! 230: #ifdef SCSIDEBUG
! 231: isp->isp_dblev |= ISP_LOGDEBUG1|ISP_LOGDEBUG2;
! 232: #endif
! 233: #ifdef DEBUG
! 234: isp->isp_dblev |= ISP_LOGDEBUG0|ISP_LOGCONFIG|ISP_LOGINFO;
! 235: #endif
! 236: #endif
! 237: isp->isp_confopts = self->dv_cfdata->cf_flags;
! 238: isp->isp_role = ISP_DEFAULT_ROLES;
! 239:
! 240: ISP_LOCK(isp);
! 241: isp->isp_osinfo.no_mbox_ints = 1;
! 242: isp_reset(isp);
! 243: if (isp->isp_state != ISP_RESETSTATE) {
! 244: ISP_UNLOCK(isp);
! 245: return;
! 246: }
! 247: ENABLE_INTS(isp);
! 248: isp_init(isp);
! 249: if (isp->isp_state != ISP_INITSTATE) {
! 250: isp_uninit(isp);
! 251: ISP_UNLOCK(isp);
! 252: return;
! 253: }
! 254:
! 255: /*
! 256: * do generic attach.
! 257: */
! 258: if (storebp) {
! 259: /*
! 260: * We're the booting HBA.
! 261: *
! 262: * Override the bootpath name with our driver name
! 263: * so we will do the correct matching and and store
! 264: * the next component's boot path entry, also so a
! 265: * successful match will occur.
! 266: */
! 267: bcopy("isp", bp->name, 4);
! 268: bp++;
! 269: bootpath_store(1, bp);
! 270: }
! 271: isp_attach(isp);
! 272: if (isp->isp_state != ISP_RUNSTATE) {
! 273: isp_uninit(isp);
! 274: }
! 275: if (storebp) {
! 276: bootpath_store(1, NULL);
! 277: }
! 278: ISP_UNLOCK(isp);
! 279: }
! 280:
! 281: #define IspVirt2Off(a, x) \
! 282: (((struct isp_sbussoftc *)a)->sbus_poff[((x) & _BLK_REG_MASK) >> \
! 283: _BLK_REG_SHFT] + ((x) & 0xff))
! 284:
! 285: #define BXR2(pcs, off) (sbc->sbus_reg[off >> 1])
! 286:
! 287: static int
! 288: isp_sbus_rd_isr(struct ispsoftc *isp, u_int16_t *isrp,
! 289: u_int16_t *semap, u_int16_t *mbp)
! 290: {
! 291: struct isp_sbussoftc *sbc = (struct isp_sbussoftc *) isp;
! 292: volatile u_int16_t isr, sema;
! 293:
! 294: isr = BXR2(pcs, IspVirt2Off(isp, BIU_ISR));
! 295: sema = BXR2(pcs, IspVirt2Off(isp, BIU_SEMA));
! 296: isp_prt(isp, ISP_LOGDEBUG3, "ISR 0x%x SEMA 0x%x", isr, sema);
! 297: isr &= INT_PENDING_MASK(isp);
! 298: sema &= BIU_SEMA_LOCK;
! 299: if (isr == 0 && sema == 0) {
! 300: return (0);
! 301: }
! 302: *isrp = isr;
! 303: if ((*semap = sema) != 0) {
! 304: *mbp = BXR2(pcs, IspVirt2Off(isp, OUTMAILBOX0));
! 305: }
! 306: return (1);
! 307: }
! 308:
! 309: static u_int16_t
! 310: isp_sbus_rd_reg(struct ispsoftc *isp, int regoff)
! 311: {
! 312: struct isp_sbussoftc *sbc = (struct isp_sbussoftc *) isp;
! 313: int offset = sbc->sbus_poff[(regoff & _BLK_REG_MASK) >> _BLK_REG_SHFT];
! 314: offset += (regoff & 0xff);
! 315: return ((u_int16_t) sbc->sbus_reg[offset >> 1]);
! 316: }
! 317:
! 318: static void
! 319: isp_sbus_wr_reg(struct ispsoftc *isp, int regoff, u_int16_t val)
! 320: {
! 321: struct isp_sbussoftc *sbc = (struct isp_sbussoftc *) isp;
! 322: int offset = sbc->sbus_poff[(regoff & _BLK_REG_MASK) >> _BLK_REG_SHFT];
! 323: offset += (regoff & 0xff);
! 324: sbc->sbus_reg[offset >> 1] = val;
! 325: }
! 326:
! 327:
! 328: static int
! 329: isp_sbus_mbxdma(struct ispsoftc *isp)
! 330: {
! 331: struct isp_sbussoftc *sbc = (struct isp_sbussoftc *) isp;
! 332: size_t len;
! 333:
! 334: if (isp->isp_rquest_dma) /* been here before? */
! 335: return (0);
! 336:
! 337: /*
! 338: * NOTE: Since most Sun machines aren't I/O coherent,
! 339: * map the mailboxes through kdvma space to force them
! 340: * to be uncached.
! 341: */
! 342:
! 343: len = isp->isp_maxcmds * sizeof (XS_T);
! 344: isp->isp_xflist = (XS_T **) malloc(len, M_DEVBUF, M_WAITOK);
! 345: bzero(isp->isp_xflist, len);
! 346: len = isp->isp_maxcmds * sizeof (vaddr_t);
! 347: sbc->sbus_kdma_allocs = (vaddr_t *) malloc(len, M_DEVBUF, M_WAITOK);
! 348: bzero(sbc->sbus_kdma_allocs, len);
! 349:
! 350: /*
! 351: * Allocate and map the request queue.
! 352: */
! 353: len = ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp));
! 354: isp->isp_rquest = (volatile caddr_t)malloc(len, M_DEVBUF, M_NOWAIT);
! 355: if (isp->isp_rquest == 0) {
! 356: printf("%s: cannot allocate request queue\n", isp->isp_name);
! 357: return (1);
! 358: }
! 359: isp->isp_rquest_dma = (u_int32_t)
! 360: kdvma_mapin((caddr_t)isp->isp_rquest, len, 0);
! 361: if (isp->isp_rquest_dma == 0) {
! 362: printf("%s: could not mapin request queue\n", isp->isp_name);
! 363: return (1);
! 364: }
! 365:
! 366: /*
! 367: * Allocate and map the result queue.
! 368: */
! 369: len = ISP_QUEUE_SIZE(RESULT_QUEUE_LEN(isp));
! 370: isp->isp_result = (volatile caddr_t)malloc(len, M_DEVBUF, M_NOWAIT);
! 371: if (isp->isp_result == 0) {
! 372: printf("%s: cannot allocate result queue\n", isp->isp_name);
! 373: return (1);
! 374: }
! 375: isp->isp_result_dma = (u_int32_t)
! 376: kdvma_mapin((caddr_t)isp->isp_result, len, 0);
! 377: if (isp->isp_result_dma == 0) {
! 378: printf("%s: could not mapin result queue\n", isp->isp_name);
! 379: return (1);
! 380: }
! 381: return (0);
! 382: }
! 383:
! 384: /*
! 385: * TODO: If kdvma_mapin fails, try using multiple smaller chunks..
! 386: */
! 387:
! 388: static int
! 389: isp_sbus_dmasetup(struct ispsoftc *isp, struct scsi_xfer *xs, ispreq_t *rq,
! 390: u_int16_t *iptrp, u_int16_t optr)
! 391: {
! 392: struct isp_sbussoftc *sbc = (struct isp_sbussoftc *) isp;
! 393: ispreq_t *qe;
! 394: ispcontreq_t *crq;
! 395: vaddr_t kdvma;
! 396: int dosleep = (xs->flags & SCSI_NOSLEEP) != 0;
! 397:
! 398: qe = (ispreq_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, isp->isp_reqidx);
! 399: if (xs->datalen == 0) {
! 400: rq->req_seg_count = 1;
! 401: goto mbxsync;
! 402: }
! 403: if (CPU_ISSUN4M) {
! 404: kdvma = (vaddr_t)
! 405: kdvma_mapin((caddr_t)xs->data, xs->datalen, dosleep);
! 406: if (kdvma == (vaddr_t) 0) {
! 407: XS_SETERR(xs, HBA_BOTCH);
! 408: return (CMD_COMPLETE);
! 409: }
! 410: } else {
! 411: kdvma = (vaddr_t) xs->data;
! 412: }
! 413:
! 414: if (sbc->sbus_kdma_allocs[isp_handle_index(rq->req_handle)] != 0) {
! 415: panic("%s: kdma handle already allocated", isp->isp_name);
! 416: /* NOTREACHED */
! 417: }
! 418: if (XS_CDBLEN(xs) > 12) {
! 419: crq = (ispcontreq_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, *iptrp);
! 420: *iptrp = ISP_NXT_QENTRY(*iptrp, RQUEST_QUEUE_LEN(isp));
! 421: if (*iptrp == optr) {
! 422: printf("%s: Request Queue Overflow++\n", isp->isp_name);
! 423: if (CPU_ISSUN4M) {
! 424: dvma_mapout(kdvma,
! 425: (vaddr_t) xs->data, xs->datalen);
! 426: }
! 427: XS_SETERR(xs, HBA_BOTCH);
! 428: return (CMD_EAGAIN);
! 429: }
! 430: } else {
! 431: crq = NULL;
! 432: }
! 433: sbc->sbus_kdma_allocs[isp_handle_index(rq->req_handle)] = kdvma;
! 434: if (xs->flags & SCSI_DATA_IN) {
! 435: rq->req_flags |= REQFLAG_DATA_IN;
! 436: } else {
! 437: rq->req_flags |= REQFLAG_DATA_OUT;
! 438: }
! 439: if (crq) {
! 440: rq->req_seg_count = 2;
! 441: rq->req_dataseg[0].ds_count = 0;
! 442: rq->req_dataseg[0].ds_base = 0;
! 443: bzero((void *)crq, sizeof (*crq));
! 444: crq->req_header.rqs_entry_count = 1;
! 445: crq->req_header.rqs_entry_type = RQSTYPE_DATASEG;
! 446: crq->req_dataseg[0].ds_count = xs->datalen;
! 447: crq->req_dataseg[0].ds_base = (u_int32_t) kdvma;
! 448: ISP_SBUSIFY_ISPHDR(isp, &crq->req_header)
! 449: } else {
! 450: rq->req_dataseg[0].ds_count = xs->datalen;
! 451: rq->req_dataseg[0].ds_base = (u_int32_t) kdvma;
! 452: rq->req_seg_count = 1;
! 453: }
! 454:
! 455: mbxsync:
! 456: ISP_SWIZZLE_REQUEST(isp, rq);
! 457: bcopy(rq, qe, sizeof (ispreq_t));
! 458: return (CMD_QUEUED);
! 459: }
! 460:
! 461: static void
! 462: isp_sbus_dmateardown(struct ispsoftc *isp, XS_T *xs, u_int16_t handle)
! 463: {
! 464: struct isp_sbussoftc *sbc = (struct isp_sbussoftc *) isp;
! 465: vaddr_t kdvma;
! 466:
! 467: if (xs->flags & SCSI_DATA_IN) {
! 468: cpuinfo.cache_flush(xs->data, xs->datalen - xs->resid);
! 469: }
! 470: if (sbc->sbus_kdma_allocs[isp_handle_index(handle)] == (vaddr_t) 0) {
! 471: panic("%s: kdma handle not already allocated", isp->isp_name);
! 472: /* NOTREACHED */
! 473: }
! 474: kdvma = sbc->sbus_kdma_allocs[isp_handle_index(handle)];
! 475: sbc->sbus_kdma_allocs[isp_handle_index(handle)] = (vaddr_t) 0;
! 476: if (CPU_ISSUN4M) {
! 477: dvma_mapout(kdvma, (vaddr_t) xs->data, xs->datalen);
! 478: }
! 479: }
! 480:
! 481: static int
! 482: isp_sbus_intr(void *arg)
! 483: {
! 484: u_int16_t isr, sema, mbox;
! 485: struct ispsoftc *isp = (struct ispsoftc *)arg;
! 486:
! 487: isp->isp_intcnt++;
! 488: if (ISP_READ_ISR(isp, &isr, &sema, &mbox) == 0) {
! 489: isp->isp_intbogus++;
! 490: return (0);
! 491: } else {
! 492: isp->isp_osinfo.onintstack = 1;
! 493: isp_intr(isp, isr, sema, mbox);
! 494: isp->isp_osinfo.onintstack = 0;
! 495: return (1);
! 496: }
! 497: }
CVSweb