|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. ! 3: * ! 4: * @APPLE_LICENSE_HEADER_START@ ! 5: * ! 6: * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights ! 7: * Reserved. This file contains Original Code and/or Modifications of ! 8: * Original Code as defined in and that are subject to the Apple Public ! 9: * Source License Version 1.1 (the "License"). You may not use this file ! 10: * except in compliance with the License. Please obtain a copy of the ! 11: * License at http://www.apple.com/publicsource and read it before using ! 12: * this file. ! 13: * ! 14: * The Original Code and all software distributed under the License are ! 15: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER ! 16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, ! 17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, ! 18: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the ! 19: * License for the specific language governing rights and limitations ! 20: * under the License. ! 21: * ! 22: * @APPLE_LICENSE_HEADER_END@ ! 23: */ ! 24: ! 25: /* Sym8xxExecute.m created by russb2 on Sat 30-May-1998 */ ! 26: ! 27: #import "Sym8xxController.h" ! 28: ! 29: /*-----------------------------------------------------------------------------* ! 30: * IOThread Routines ! 31: * ! 32: * This module contains routines that run on the driver's IOThread. ! 33: * ! 34: *-----------------------------------------------------------------------------*/ ! 35: ! 36: @implementation Sym8xxController(Execute) ! 37: ! 38: /*-----------------------------------------------------------------------------* ! 39: * This is the main command processing loop for the driver's I/O thread. ! 40: * It removes SRBs from the driver's command queue and processes them according ! 41: * to the command code in srb->srbCmd. ! 42: * ! 43: *-----------------------------------------------------------------------------*/ ! 44: - (void) commandRequestOccurred ! 45: { ! 46: SRB *srb = NULL; ! 47: ! 48: while ( 1 ) ! 49: { ! 50: [srbPendingQLock lock]; ! 51: ! 52: if ( queue_empty(&srbPendingQ) ) ! 53: { ! 54: [srbPendingQLock unlock]; ! 55: break; ! 56: } ! 57: queue_remove_first( &srbPendingQ, srb, SRB *, srbQ ); ! 58: ! 59: [srbPendingQLock unlock]; ! 60: ! 61: ! 62: /* ! 63: * If we are in the quiet period after a SCSI Bus reset, then reject new SRBs. In ! 64: * general the client thread processing will block new SRBs, however, some may have ! 65: * been on the IOThread's SRB queue when the reset occurred. ! 66: */ ! 67: if ( resetQuiesceTimer ) ! 68: { ! 69: srb->srbSCSIResult = SR_IOST_RESET; ! 70: [srb->srbCmdLock unlockWith: ksrbCmdComplete]; ! 71: continue; ! 72: } ! 73: ! 74: switch ( srb->srbCmd ) ! 75: { ! 76: /* ! 77: * For a SCSI CDB request, stuff the physical address of the SRB's Nexus struct into a ! 78: * mailbox and signal the Symbios script engine. ! 79: */ ! 80: case ksrbCmdExecuteReq: ! 81: srb->nexus.targetParms.scntl3Reg = adapter->targetClocks[srb->target].scntl3Reg; ! 82: srb->nexus.targetParms.sxferReg = adapter->targetClocks[srb->target].sxferReg; ! 83: ! 84: adapter->nexusPtrsVirt[srb->nexus.tag] = &srb->nexus; ! 85: adapter->nexusPtrsPhys[srb->nexus.tag] = (Nexus *)EndianSwap32( (u_int32_t)&srb->srbPhys->nexus ); ! 86: adapter->schedMailBox[mailBoxIndex++] = (Nexus *)EndianSwap32 ( (u_int32_t)&srb->srbPhys->nexus ); ! 87: ! 88: [self Sym8xxSignalScript: srb]; ! 89: break; ! 90: ! 91: case ksrbCmdResetSCSIBus: ! 92: [self Sym8xxSCSIBusReset: srb]; ! 93: break; ! 94: ! 95: case ksrbCmdAbortReq: ! 96: case ksrbCmdBusDevReset: ! 97: [self Sym8xxAbortBdr: srb]; ! 98: break; ! 99: ! 100: default: ! 101: ; ! 102: } ! 103: } ! 104: } ! 105: ! 106: ! 107: /*-----------------------------------------------------------------------------* ! 108: * Interrupts from the Symbios chipset are dispatched here at task time under the ! 109: * IOThread's context. ! 110: *-----------------------------------------------------------------------------*/ ! 111: - (void) interruptOccurred ! 112: { ! 113: do ! 114: { ! 115: /* ! 116: * The chipset's ISTAT reg gives us the general interrupting condiditions, ! 117: * with DSTAT and SIST providing more detailed information. ! 118: */ ! 119: istatReg = Sym8xxReadRegs( chipBaseAddr, ISTAT, ISTAT_SIZE ); ! 120: ! 121: /* The INTF bit in ISTAT indicates that the script is signalling the driver ! 122: * that its IODone mailbox is full and that we should process a completed ! 123: * request. The script continues to run after posting this interrupt unlike ! 124: * other chipset interrupts which require the driver to restart the script ! 125: * engine. ! 126: */ ! 127: if ( istatReg & INTF ) ! 128: { ! 129: Sym8xxWriteRegs( chipBaseAddr, ISTAT, ISTAT_SIZE, istatReg ); ! 130: [self Sym8xxProcessIODone]; ! 131: } ! 132: ! 133: /* ! 134: * Handle remaining interrupting conditions ! 135: */ ! 136: if ( istatReg & (SIP | DIP) ) ! 137: { ! 138: [self Sym8xxProcessInterrupt]; ! 139: } ! 140: } ! 141: while ( istatReg & (SIP | DIP | INTF) ); ! 142: ! 143: [self enableAllInterrupts]; ! 144: ! 145: } ! 146: ! 147: /*-----------------------------------------------------------------------------* ! 148: * Process a request posted in the script's IODone mailbox. ! 149: * ! 150: *-----------------------------------------------------------------------------*/ ! 151: - (void) Sym8xxProcessIODone ! 152: { ! 153: SRB *srb; ! 154: Nexus *nexus; ! 155: IODoneMailBox *pMailBox; ! 156: ! 157: ! 158: /* ! 159: * The IODone mailbox contains an index into our Nexus pointer tables. ! 160: * ! 161: * The Nexus struct is part of the SRB so we can get our SRB address ! 162: * by subtracting the offset of the Nexus struct in the SRB. ! 163: */ ! 164: pMailBox = (IODoneMailBox *)&SCRIPT_VAR(R_ld_IOdone_mailbox); ! 165: nexus = adapter->nexusPtrsVirt[pMailBox->nexus]; ! 166: srb = (SRB *)((u_int32_t)nexus - offsetof(SRB, nexus)); ! 167: ! 168: /* ! 169: * If there was no request sense performed, then update the transfer ! 170: * counts in the SRB. ! 171: */ ! 172: if ( srb->srbState == ksrbStateCDBDone ) ! 173: { ! 174: [self Sym8xxUpdateXferOffset: srb]; ! 175: } ! 176: ! 177: /* ! 178: * Clear the completed Nexus pointer from our tables and clear the ! 179: * IODone mailbox. ! 180: */ ! 181: adapter->nexusPtrsVirt[pMailBox->nexus] = (Nexus *) -1; ! 182: adapter->nexusPtrsPhys[pMailBox->nexus] = (Nexus *) -1; ! 183: SCRIPT_VAR(R_ld_IOdone_mailbox) = 0; ! 184: ! 185: /* ! 186: * Don't ask why we need to do this -- we shouldn't if the Indirect SCSI ! 187: * drivers worked properly! ! 188: * ! 189: */ ! 190: if ( nexus->cdbData.cdb_opcode == C6OP_INQUIRY ) ! 191: { ! 192: [self Sym8xxCheckInquiryData: srb]; ! 193: } ! 194: ! 195: /* ! 196: * Wake up the client's thread to do post-processing ! 197: */ ! 198: [srb->srbCmdLock unlockWith: ksrbCmdComplete]; ! 199: } ! 200: ! 201: /*-----------------------------------------------------------------------------* ! 202: * General script interrupt processing ! 203: * ! 204: *-----------------------------------------------------------------------------*/ ! 205: - (void) Sym8xxProcessInterrupt ! 206: { ! 207: SRB *srb = NULL; ! 208: Nexus *nexus = NULL; ! 209: u_int32_t nexusIndex; ! 210: u_int32_t scriptPhase; ! 211: u_int32_t fifoCnt = 0; ! 212: u_int32_t dspsReg = 0; ! 213: u_int32_t dspReg = 0; ! 214: ! 215: ! 216: /* ! 217: * Read DSTAT/SIST regs to determine why the script stopped. ! 218: */ ! 219: dstatReg = Sym8xxReadRegs( chipBaseAddr, DSTAT, DSTAT_SIZE ); ! 220: IODelay(5); ! 221: sistReg = Sym8xxReadRegs( chipBaseAddr, SIST, SIST_SIZE ); ! 222: ! 223: // kprintf( "SCSI(Symbios8xx): SIST = %04x DSTAT = %02x\n\r", sistReg, dstatReg ); ! 224: ! 225: /* ! 226: * This Script var tells us what the script thinks it was doing when the interrupt occurred. ! 227: */ ! 228: scriptPhase = EndianSwap32( SCRIPT_VAR(R_ld_phase_flag) ); ! 229: ! 230: /* ! 231: * SCSI Bus reset detected ! 232: * ! 233: * Clean up the carnage. ! 234: * Note: This may be either an adapter or target initiated reset. ! 235: */ ! 236: if ( sistReg & RSTI ) ! 237: { ! 238: [self Sym8xxProcessSCSIBusReset]; ! 239: return; ! 240: } ! 241: ! 242: /* ! 243: * Calculate our current SRB/Nexus. ! 244: * ! 245: * Read a script var to determine the index of the nexus it was processing ! 246: * when the interrupt occurred. The script will invalidate the index if there ! 247: * is no target currently connected or the script cannot determine which target ! 248: * has reconnected. ! 249: */ ! 250: nexusIndex = EndianSwap32(SCRIPT_VAR(R_ld_nexus_index)); ! 251: if ( nexusIndex >= MAX_SCSI_TAG ) ! 252: { ! 253: [self Sym8xxProcessNoNexus]; ! 254: return; ! 255: } ! 256: nexus = adapter->nexusPtrsVirt[nexusIndex]; ! 257: if ( nexus == (Nexus *) -1 ) ! 258: { ! 259: [self Sym8xxProcessNoNexus]; ! 260: return; ! 261: } ! 262: srb = (SRB *)((u_int32_t)nexus - offsetof(SRB, nexus)); ! 263: ! 264: scriptRestartAddr = (u_int32_t) &chipRamAddrPhys[Ent_phase_handler]; ! 265: ! 266: /* ! 267: * Parity and SCSI Gross Errors. ! 268: * ! 269: * Abort the current connection. The abort completion will trigger ! 270: * clean-up of the current SRB/Nexus. ! 271: */ ! 272: if ( sistReg & PAR ) ! 273: { ! 274: srb->srbSCSIResult = SR_IOST_PARITY; ! 275: [self Sym8xxAbortCurrent: srb]; ! 276: } ! 277: ! 278: else if ( sistReg & SGE ) ! 279: { ! 280: srb->srbSCSIResult = SR_IOST_BV; ! 281: [self Sym8xxAbortCurrent: srb]; ! 282: } ! 283: ! 284: /* ! 285: * Unexpected disconnect. ! 286: * ! 287: * If we were currently trying to abort this connection then mark the abort ! 288: * as completed. For all cases clean-up and wake-up the client thread. ! 289: */ ! 290: else if ( sistReg & UDC ) ! 291: { ! 292: if ( srb->srbSCSIResult == SR_IOST_GOOD ) ! 293: { ! 294: srb->srbSCSIResult = SR_IOST_BV; ! 295: } ! 296: adapter->nexusPtrsVirt[nexusIndex] = (Nexus *) -1; ! 297: adapter->nexusPtrsPhys[nexusIndex] = (Nexus *) -1; ! 298: ! 299: if ( scriptPhase == A_kphase_ABORT_CURRENT ) ! 300: { ! 301: abortCurrentSRB = NULL; ! 302: } ! 303: ! 304: [srb->srbCmdLock unlockWith: ksrbCmdComplete]; ! 305: ! 306: scriptRestartAddr = (u_int32_t) &chipRamAddrPhys[Ent_select_phase]; ! 307: } ! 308: ! 309: /* ! 310: * Phase Mis-match ! 311: * ! 312: * If we are in MsgOut phase then calculate how much of the message we sent. For ! 313: * now, however, we dont handle the target rejecting messages, so the request is aborted. ! 314: * ! 315: * If we are in DataIn/DataOut phase. We update the SRB/Nexus with our current data ! 316: * pointers. ! 317: */ ! 318: else if ( sistReg & MA ) ! 319: { ! 320: if ( scriptPhase == A_kphase_MSG_OUT ) ! 321: { ! 322: srb->srbMsgResid = [self Sym8xxCheckFifo:srb FifoCnt:&fifoCnt]; ! 323: nexus->msg.ppData = EndianSwap32( EndianSwap32(nexus->msg.ppData) + EndianSwap32(nexus->msg.length) ! 324: - srb->srbMsgResid ); ! 325: nexus->msg.length = EndianSwap32( srb->srbMsgResid ); ! 326: ! 327: [self Sym8xxAbortCurrent: srb]; ! 328: } ! 329: else if ( (scriptPhase == A_kphase_DATA_OUT) || (scriptPhase == A_kphase_DATA_IN) ) ! 330: { ! 331: [self Sym8xxAdjustDataPtrs:srb Nexus:nexus]; ! 332: } ! 333: else ! 334: { ! 335: // kprintf("SCSI(Symbios8xx): Unexpected phase mismatch - scriptPhase = %08x\n\r", scriptPhase); ! 336: srb->srbSCSIResult = SR_IOST_BV; ! 337: [self Sym8xxAbortCurrent: srb]; ! 338: } ! 339: ! 340: [self Sym8xxClearFifo]; ! 341: } ! 342: ! 343: /* ! 344: * Selection Timeout. ! 345: * ! 346: * Clean-up the current request. ! 347: */ ! 348: else if ( sistReg & STO ) ! 349: { ! 350: srb->srbSCSIResult = SR_IOST_SELTO; ! 351: ! 352: adapter->nexusPtrsVirt[nexusIndex] = (Nexus *) -1; ! 353: adapter->nexusPtrsPhys[nexusIndex] = (Nexus *) -1; ! 354: SCRIPT_VAR(R_ld_IOdone_mailbox) = 0; ! 355: ! 356: [srb->srbCmdLock unlockWith: ksrbCmdComplete]; ! 357: ! 358: scriptRestartAddr = (u_int32_t) &chipRamAddrPhys[Ent_select_phase]; ! 359: } ! 360: ! 361: /* ! 362: * Handle script initiated interrupts ! 363: */ ! 364: else if ( dstatReg & SIR ) ! 365: { ! 366: dspsReg = Sym8xxReadRegs( chipBaseAddr, DSPS, DSPS_SIZE ); ! 367: ! 368: switch ( dspsReg ) ! 369: { ! 370: /* ! 371: * Non-zero SCSI status ! 372: * ! 373: * Send request sense CDB or complete request depending on SCSI status value ! 374: */ ! 375: case A_status_error: ! 376: if ( [self Sym8xxProcessStatus:srb] == NO ) ! 377: { ! 378: [self Sym8xxProcessIODone]; ! 379: } ! 380: break; ! 381: ! 382: /* ! 383: * Received SDTR/WDTR message from target. ! 384: * ! 385: * Prepare reply message if we requested negotiation. Otherwise reject ! 386: * target initiated negotiation. ! 387: */ ! 388: case A_negotiateSDTR: ! 389: [self Sym8xxNegotiateSDTR:srb Nexus: nexus]; ! 390: break; ! 391: ! 392: case A_negotiateWDTR: ! 393: [self Sym8xxNegotiateWDTR:srb Nexus: nexus]; ! 394: break; ! 395: ! 396: /* ! 397: * Partial SG List completed. ! 398: * ! 399: * Refresh the list from the remaining addresses to be transfered and set the ! 400: * script engine to branch into the list. ! 401: */ ! 402: case A_sglist_complete: ! 403: [self Sym8xxUpdateSGList:srb]; ! 404: scriptRestartAddr = (u_int32_t)&srb->srbPhys->nexus.sgListData[2]; ! 405: break; ! 406: ! 407: /* ! 408: * Completed abort request ! 409: * ! 410: * Clean-up the aborted request. ! 411: */ ! 412: case A_abort_current: ! 413: if ( srb->srbSCSIResult == SR_IOST_GOOD ) ! 414: { ! 415: srb->srbSCSIResult = SR_IOST_BV; ! 416: } ! 417: ! 418: adapter->nexusPtrsVirt[nexusIndex] = (Nexus *) -1; ! 419: adapter->nexusPtrsPhys[nexusIndex] = (Nexus *) -1; ! 420: ! 421: abortCurrentSRB = NULL; ! 422: ! 423: [srb->srbCmdLock unlockWith: ksrbCmdComplete]; ! 424: ! 425: scriptRestartAddr = (u_int32_t) &chipRamAddrPhys[Ent_select_phase]; ! 426: break; ! 427: ! 428: /* ! 429: * Script detected protocol errors ! 430: * ! 431: * Abort the current request. ! 432: */ ! 433: case A_unknown_msg_reject: ! 434: case A_unknown_phase: ! 435: case A_unexpected_msg: ! 436: case A_unexpected_ext_msg: ! 437: srb->srbSCSIResult = SR_IOST_TABT; ! 438: [self Sym8xxAbortCurrent: srb]; ! 439: break; ! 440: ! 441: default: ! 442: kprintf( "SCSI(Symbios8xx): Unknown Script Int = %08x\n\r", dspsReg ); ! 443: srb->srbSCSIResult = SR_IOST_INT; ! 444: [self Sym8xxAbortCurrent: srb]; ! 445: } ! 446: } ! 447: ! 448: /* ! 449: * Illegal script instruction. ! 450: * ! 451: * We're toast! Abort the current request and hope for the best! ! 452: */ ! 453: else if ( dstatReg & IID ) ! 454: { ! 455: dspReg = Sym8xxReadRegs( chipBaseAddr, DSP, DSP_SIZE ); ! 456: ! 457: kprintf("SCSI(Symbios8xx): Illegal script instruction - dsp = %08x srb=%08x\n\r", dspReg, (u_int32_t)srb ); ! 458: ! 459: srb->srbSCSIResult = SR_IOST_INT; ! 460: [self Sym8xxAbortCurrent: srb]; ! 461: } ! 462: ! 463: if ( scriptRestartAddr ) ! 464: { ! 465: Sym8xxWriteRegs( chipBaseAddr, DSP, DSP_SIZE, scriptRestartAddr ); ! 466: } ! 467: } ! 468: ! 469: /*-----------------------------------------------------------------------------* ! 470: * Handle non-zero SCSI status ! 471: * ! 472: * Returns: ! 473: * NO - Clean-up request now. ! 474: * YES - Wait for request sense to complete. ! 475: * ! 476: * This routine filter's out BUSY and more arcane SCSI status conditions, ! 477: * leaving CHECK_CONDITION, for which it set's up a request sense operation. ! 478: * ! 479: *-----------------------------------------------------------------------------*/ ! 480: - (BOOL) Sym8xxProcessStatus: (SRB *) srb; ! 481: { ! 482: IODoneMailBox *pMailBox; ! 483: ! 484: scriptRestartAddr = (u_int32_t) &chipRamAddrPhys[Ent_select_phase]; ! 485: ! 486: pMailBox = (IODoneMailBox *)&SCRIPT_VAR(R_ld_IOdone_mailbox); ! 487: ! 488: /* ! 489: * If a previous request sense failed, then clean-up the request now. ! 490: */ ! 491: if ( srb->srbState != ksrbStateCDBDone ) ! 492: { ! 493: if ( srb->srbSCSIResult == SR_IOST_GOOD ) ! 494: { ! 495: srb->srbSCSIResult = SR_IOST_CHKSNV; ! 496: } ! 497: return NO; ! 498: } ! 499: ! 500: /* ! 501: * Update the SRB with our byte transferred count and SCSI status. ! 502: * This information needs to be captured before we issue the request ! 503: * sense. ! 504: */ ! 505: srb->srbSCSIStatus = pMailBox->status; ! 506: ! 507: [self Sym8xxUpdateXferOffset: srb]; ! 508: ! 509: if ( pMailBox->status != STAT_CHECK ) ! 510: { ! 511: srb->srbSCSIResult = ST_IOST_BADST; ! 512: return NO; ! 513: } ! 514: if ( srb->senseData == NULL ) ! 515: { ! 516: srb->srbSCSIResult = SR_IOST_CHKSNV; ! 517: return NO; ! 518: } ! 519: ! 520: [self Sym8xxIssueRequestSense: srb]; ! 521: ! 522: return YES; ! 523: } ! 524: ! 525: /*-----------------------------------------------------------------------------* ! 526: * Prepare request sense request. ! 527: * ! 528: *-----------------------------------------------------------------------------*/ ! 529: - (void) Sym8xxIssueRequestSense:(SRB *) srb ! 530: { ! 531: IODoneMailBox *pMailBox; ! 532: u_int32_t reqSenseMailBox; ! 533: ! 534: pMailBox = (IODoneMailBox *)&SCRIPT_VAR(R_ld_IOdone_mailbox); ! 535: ! 536: /* ! 537: * We put the request sense Nexus in the last completed script mailbox and ! 538: * back-up the script's mailbox pointer. ! 539: */ ! 540: reqSenseMailBox = (u_int8_t)(EndianSwap32(SCRIPT_VAR(R_ld_counter)) - 1); ! 541: SCRIPT_VAR(R_ld_counter) = EndianSwap32( reqSenseMailBox ); ! 542: ! 543: srb->srbTimeout = kReqSenseTimeoutMS / kSCSITimerIntervalMS + 1; ! 544: ! 545: srb->srbState = ksrbStateReqSenseDone; ! 546: srb->srbSCSIResult = SR_IOST_CHKSV; ! 547: ! 548: /* ! 549: * Reuse the original Nexus struct. The original CDB is not preserved. The ! 550: * original status and transfer counts and tag are kept in the SRB. ! 551: */ ! 552: bzero( &srb->nexus.cdbData, 6 ); ! 553: srb->nexus.cdbData.cdb_c6.c6_opcode = C6OP_REQSENSE; ! 554: srb->nexus.cdbData.cdb_c6.c6_lun = srb->lun; ! 555: srb->nexus.cdbData.cdb_c6.c6_len = srb->senseDataLength; ! 556: srb->nexus.cdb.length = EndianSwap32( 6 ); ! 557: ! 558: /* ! 559: * Force renegotiation on request sense. ! 560: */ ! 561: targets[srb->target].flags &= ~(kTFXferSync | kTFXferWide16); ! 562: srb->srbRequestFlags &= ~(ksrbRFCmdQueueAllowed | ksrbRFDisconnectAllowed); ! 563: [self Sym8xxCalcMsgs:srb]; ! 564: ! 565: /* ! 566: * Create a new SG List for the request sense data ! 567: * ! 568: */ ! 569: srb->xferOffset = 0; ! 570: srb->xferOffsetPrev = 0; ! 571: srb->xferClient = IOVmTaskSelf(); ! 572: srb->xferBuffer = srb->senseData; ! 573: srb->xferCount = srb->senseDataLength; ! 574: srb->directionMask = 0x01000000; ! 575: srb->nexus.ppSGList = (SGEntry *)EndianSwap32((u_int32_t)&srb->srbPhys->nexus.sgListData[2]); ! 576: [self Sym8xxUpdateSGList: srb]; ! 577: ! 578: /* ! 579: * If the original request was using cmd-queuing, we clean-up the original tagged request ! 580: * and convert it to a non-tagged request sense. ! 581: */ ! 582: if ( srb->nexus.tag >= MIN_SCSI_TAG ) ! 583: { ! 584: adapter->nexusPtrsVirt[pMailBox->nexus] = (Nexus *) -1; ! 585: adapter->nexusPtrsPhys[pMailBox->nexus] = (Nexus *) -1; ! 586: ! 587: srb->nexus.tag = (srb->target << 3) | srb->lun; ! 588: adapter->nexusPtrsVirt[srb->nexus.tag] = &srb->nexus; ! 589: adapter->nexusPtrsPhys[srb->nexus.tag] = (Nexus *)EndianSwap32( (u_int32_t)&srb->srbPhys->nexus ); ! 590: } ! 591: ! 592: adapter->schedMailBox[reqSenseMailBox] = (Nexus *)EndianSwap32 ( (u_int32_t)&srb->srbPhys->nexus ); ! 593: ! 594: [self Sym8xxSignalScript: srb]; ! 595: ! 596: SCRIPT_VAR(R_ld_IOdone_mailbox) = 0; ! 597: } ! 598: ! 599: /*-----------------------------------------------------------------------------* ! 600: * Current Data Pointer calculations ! 601: * ! 602: * To do data transfers the driver generates a list of script instructions ! 603: * in system storage to deliver data to the requested physical addresses. The ! 604: * script branches to the list when the target enters data transfer phase. ! 605: * ! 606: * When the target changes phase during a data transfer, data is left trapped ! 607: * inside the various script engine registers. This routine determines how much ! 608: * data was not actually transfered to/from the target and generates a new ! 609: * S/G List entry for the partial transfer and a branch back into the original ! 610: * S/G list. These script instructions are stored in two reserved slots at the ! 611: * top of the original S/G List. ! 612: * ! 613: *-----------------------------------------------------------------------------*/ ! 614: - (void) Sym8xxAdjustDataPtrs:(SRB *) srb Nexus:(Nexus *) nexus ! 615: { ! 616: u_int32_t i; ! 617: u_int32_t sgResid; ! 618: u_int32_t fifoCnt; ! 619: u_int32_t dspReg; ! 620: u_int32_t sgDone; ! 621: u_int8_t scntl2Reg; ! 622: Nexus *nexusPhys; ! 623: ! 624: /* ! 625: * Determine SG element residual ! 626: * ! 627: * This routine returns how much of the current S/G List element the ! 628: * script was processing remains to be sent/received. All the information ! 629: * required to do this is stored in the script engine's registers. ! 630: */ ! 631: sgResid = [self Sym8xxCheckFifo:srb FifoCnt:&fifoCnt]; ! 632: ! 633: /* ! 634: * Determine which script instruction in our SGList we were executing when ! 635: * the target changed phase. ! 636: * ! 637: * The script engine's dspReg tells us where the script thinks it was. Based ! 638: * on the physical address of our current SRB/Nexus we can calculate ! 639: * an index into our S/G List. ! 640: */ ! 641: dspReg = Sym8xxReadRegs( chipBaseAddr, DSP, DSP_SIZE ); ! 642: ! 643: i = ((dspReg - (u_int32_t)srb->srbPhys->nexus.sgListData) / sizeof(SGEntry)) - 1; ! 644: ! 645: if ( i > MAX_SGLIST_ENTRIES-1 ) ! 646: { ! 647: kprintf("SCSI(Symbios8xx): Bad sgListIndex\n\r"); ! 648: [self Sym8xxAbortCurrent: srb]; ! 649: return; ! 650: } ! 651: ! 652: /* ! 653: * Wide/odd-byte transfers. ! 654: * ! 655: * When dealing with Wide data transfers, if a S/G List ends with an odd-transfer count, then a ! 656: * valid received data byte is left in the script engine's SWIDE register. The least painful way ! 657: * to recover this byte is to construct a small script thunk to transfer one additional byte. The ! 658: * script will automatically draw this byte from the SWIDE register rather than the SCSI bus. ! 659: * The script thunk then branches back to script's PhaseHandler entrypoint. ! 660: * ! 661: */ ! 662: nexusPhys = &srb->srbPhys->nexus; ! 663: ! 664: scntl2Reg = Sym8xxReadRegs( chipBaseAddr, SCNTL2, SCNTL2_SIZE ); ! 665: if ( scntl2Reg & WSR ) ! 666: { ! 667: adapter->xferSWideInst[0] = EndianSwap32( srb->directionMask | 1 ); ! 668: adapter->xferSWideInst[1] = nexus->sgListData[i].physAddr; ! 669: adapter->xferSWideInst[2] = EndianSwap32( 0x80080000 ); ! 670: adapter->xferSWideInst[3] = EndianSwap32( (u_int32_t)&chipRamAddrPhys[Ent_phase_handler] ); ! 671: ! 672: scriptRestartAddr = (u_int32_t) adapterPhys->xferSWideInst; ! 673: ! 674: /* ! 675: * Note: There is an assumption here that the sgResid count will be > 1. It appears ! 676: * that the script engine does not generate a phase-mismatch interrupt until ! 677: * we attempt to move > 1 byte from the SCSI bus and the only byte available is ! 678: * in SWIDE. ! 679: */ ! 680: sgResid--; ! 681: } ! 682: ! 683: /* ! 684: * Calculate partial S/G List instruction and branch ! 685: * ! 686: * Fill in slots 0/1 of the SGList based on the SGList index (i) and SGList residual count ! 687: * (sgResid) calculated above. ! 688: * ! 689: */ ! 690: sgDone = (EndianSwap32( nexus->sgListData[i].length ) & 0x00ffffff) - sgResid; ! 691: ! 692: nexus->sgListData[0].length = EndianSwap32( sgResid | srb->directionMask ); ! 693: nexus->sgListData[0].physAddr = EndianSwap32( EndianSwap32(nexus->sgListData[i].physAddr) + sgDone ); ! 694: /* ! 695: * If a previously calculated SGList 0 entry was interrupted again, we dont need to calculate ! 696: * a new branch address since the previous one is still valid. ! 697: */ ! 698: if ( i != 0 ) ! 699: { ! 700: nexus->sgListData[1].length = EndianSwap32( 0x80080000 ); ! 701: nexus->sgListData[1].physAddr = EndianSwap32( (u_int32_t)&nexusPhys->sgListData[i+1] ); ! 702: nexus->sgNextIndex = i + 1; ! 703: } ! 704: nexus->ppSGList = (SGEntry *)EndianSwap32( (u_int32_t) &nexusPhys->sgListData[0] ); ! 705: ! 706: /* ! 707: * The script sets this Nexus variable to non-zero each time it calls the driver generated ! 708: * S/G list. This allows the driver's completion routines to differentiate between a successful ! 709: * transfer vs no data transfer at all. ! 710: */ ! 711: nexus->dataXferCalled = 0; ! 712: ! 713: return; ! 714: } ! 715: ! 716: /*-----------------------------------------------------------------------------* ! 717: * Determine SG element residual ! 718: * ! 719: * This routine returns how much of the current S/G List element the ! 720: * script was processing remains to be sent/received. All the information ! 721: * required to do this is stored in the script engine's registers. ! 722: * ! 723: *-----------------------------------------------------------------------------*/ ! 724: - (u_int32_t) Sym8xxCheckFifo: (SRB *) srb FifoCnt:(u_int32_t *)pfifoCnt ! 725: { ! 726: BOOL fSCSISend; ! 727: BOOL fXferSync; ! 728: u_int32_t scriptPhase = 0; ! 729: u_int32_t dbcReg = 0; ! 730: u_int32_t dfifoReg = 0; ! 731: u_int32_t ctest5Reg = 0; ! 732: u_int8_t sstat0Reg = 0; ! 733: u_int8_t sstat1Reg = 0; ! 734: u_int8_t sstat2Reg = 0; ! 735: u_int32_t fifoCnt = 0; ! 736: u_int32_t sgResid = 0; ! 737: ! 738: scriptPhase = EndianSwap32( SCRIPT_VAR(R_ld_phase_flag) ); ! 739: ! 740: fSCSISend = (scriptPhase == A_kphase_DATA_OUT) || (scriptPhase == A_kphase_MSG_OUT); ! 741: ! 742: fXferSync = ((scriptPhase == A_kphase_DATA_OUT) || (scriptPhase == A_kphase_DATA_IN)) ! 743: && (srb->nexus.targetParms.sxferReg & 0x1F); ! 744: ! 745: dbcReg = Sym8xxReadRegs( chipBaseAddr, DBC, DBC_SIZE ) & 0x00ffffff; ! 746: ! 747: if ( !(dstatReg & DFE) ) ! 748: { ! 749: ctest5Reg = Sym8xxReadRegs( chipBaseAddr, CTEST5, CTEST5_SIZE ); ! 750: dfifoReg = Sym8xxReadRegs( chipBaseAddr, DFIFO, DFIFO_SIZE ); ! 751: ! 752: if ( ctest5Reg & DFS ) ! 753: { ! 754: fifoCnt = ((((ctest5Reg & 0x03) << 8) | dfifoReg) - dbcReg) & 0x3ff; ! 755: } ! 756: else ! 757: { ! 758: fifoCnt = (dfifoReg - dbcReg) & 0x7f; ! 759: } ! 760: } ! 761: ! 762: sstat0Reg = Sym8xxReadRegs( chipBaseAddr, SSTAT0, SSTAT0_SIZE ); ! 763: sstat2Reg = Sym8xxReadRegs( chipBaseAddr, SSTAT2, SSTAT2_SIZE ); ! 764: ! 765: if ( fSCSISend ) ! 766: { ! 767: fifoCnt += (sstat0Reg & OLF ) ? 1 : 0; ! 768: fifoCnt += (sstat2Reg & OLF1) ? 1 : 0; ! 769: ! 770: if ( fXferSync ) ! 771: { ! 772: fifoCnt += (sstat0Reg & ORF ) ? 1 : 0; ! 773: fifoCnt += (sstat2Reg & ORF1) ? 1 : 0; ! 774: } ! 775: } ! 776: else ! 777: { ! 778: if ( fXferSync ) ! 779: { ! 780: sstat1Reg = Sym8xxReadRegs( chipBaseAddr, SSTAT0, SSTAT0_SIZE ); ! 781: fifoCnt += (sstat1Reg >> 4) | (sstat2Reg & FF4); ! 782: } ! 783: else ! 784: { ! 785: fifoCnt += (sstat0Reg & ILF ) ? 1 : 0; ! 786: fifoCnt += (sstat2Reg & ILF1) ? 1 : 0; ! 787: } ! 788: } ! 789: ! 790: sgResid = dbcReg + fifoCnt; ! 791: *pfifoCnt = fifoCnt; ! 792: ! 793: return sgResid; ! 794: } ! 795: ! 796: /*-----------------------------------------------------------------------------* ! 797: * Calculate transfer counts. ! 798: * ! 799: * This routine updates srb->xferDone with the amount of data transferred ! 800: * by the last S/G List executed. ! 801: * ! 802: *-----------------------------------------------------------------------------*/ ! 803: - (void) Sym8xxUpdateXferOffset:(SRB *) srb ! 804: { ! 805: u_int32_t i; ! 806: u_int32_t xferOffset; ! 807: ! 808: /* ! 809: * srb->xferOffset contains the client buffer offset INCLUDING the range ! 810: * covered by the current SGList. ! 811: */ ! 812: xferOffset = srb->xferOffset; ! 813: ! 814: /* ! 815: * If script did not complete the current transfer list then we need to determine ! 816: * how much of the list was completed. ! 817: */ ! 818: if ( srb->nexus.dataXferCalled == 0 ) ! 819: { ! 820: /* ! 821: * srb->xferOffsetPrev contains the client buffer offset EXCLUDING the ! 822: * range covered by the current SGList. ! 823: */ ! 824: xferOffset = srb->xferOffsetPrev; ! 825: ! 826: /* ! 827: * Calculate bytes transferred for partially completed list. ! 828: * ! 829: * To calculate the amount of this list completed, we sum the residual amount ! 830: * in SGList Slot 0 and the completed list elements 2 to sgNextIndex-1. ! 831: */ ! 832: if ( srb->nexus.sgNextIndex != 0 ) ! 833: { ! 834: xferOffset += EndianSwap32( srb->nexus.sgListData[srb->nexus.sgNextIndex-1].length ) ! 835: - EndianSwap32( srb->nexus.sgListData[0].length ); ! 836: ! 837: for ( i=2; i < srb->nexus.sgNextIndex-1; i++ ) ! 838: { ! 839: xferOffset += EndianSwap32( srb->nexus.sgListData[i].length ) & 0x00ffffff; ! 840: } ! 841: } ! 842: } ! 843: ! 844: /* ! 845: * The script leaves the result of any Ignore Wide Residual message received from the target ! 846: * during the transfer. ! 847: */ ! 848: xferOffset -= srb->nexus.wideResidCount; ! 849: ! 850: ! 851: #if 0 ! 852: { ! 853: u_int32_t resid = srb->xferOffset - xferOffset; ! 854: if ( resid ) ! 855: { ! 856: kprintf( "SCSI(Symbios8xx): Incomplete transfer - Req Count = %08x Act Count = %08x - srb = %08x\n\r", ! 857: srb->xferCount, xferOffset, (u_int32_t)srb ); ! 858: } ! 859: } ! 860: #endif ! 861: ! 862: srb->xferDone = xferOffset; ! 863: } ! 864: ! 865: /*-----------------------------------------------------------------------------* ! 866: * No SRB/Nexus Processing. ! 867: * ! 868: * In some cases (mainly Aborts) not having a SRB/Nexus is normal. In other ! 869: * cases it indicates a problem such a reconnection from a target that we ! 870: * have no record of. ! 871: * ! 872: *-----------------------------------------------------------------------------*/ ! 873: - (void) Sym8xxProcessNoNexus ! 874: { ! 875: u_int32_t dspsReg; ! 876: u_int32_t dspReg = 0; ! 877: u_int32_t scriptPhase = -1 ; ! 878: ! 879: scriptRestartAddr = (u_int32_t) &chipRamAddrPhys[Ent_select_phase]; ! 880: ! 881: dspsReg = Sym8xxReadRegs( chipBaseAddr, DSPS, DSPS_SIZE ); ! 882: ! 883: scriptPhase = EndianSwap32( SCRIPT_VAR(R_ld_phase_flag) ); ! 884: ! 885: /* ! 886: * If we were trying to abort or disconnect a target and the bus ! 887: * is now free we consider the abort to have completed. ! 888: */ ! 889: if ( sistReg & UDC ) ! 890: { ! 891: if ( (scriptPhase == A_kphase_ABORT_MAILBOX) && abortSRB ) ! 892: { ! 893: [abortSRB->srbCmdLock unlockWith: ksrbCmdComplete]; ! 894: abortSRB = (SRB *) NULL; ! 895: SCRIPT_VAR(R_ld_AbortBdr_mailbox) = 0; ! 896: } ! 897: else if ( scriptPhase == A_kphase_ABORT_CURRENT ) ! 898: { ! 899: abortCurrentSRB = NULL; ! 900: } ! 901: } ! 902: /* ! 903: * If we were trying to connect to a target to send it an abort message, and ! 904: * we timed out, we consider the abort as completed. ! 905: * ! 906: * Note: In this case the target may be hung, but at least its not on the bus. ! 907: */ ! 908: else if ( sistReg & STO ) ! 909: { ! 910: if ( (scriptPhase == A_kphase_ABORT_MAILBOX) && abortSRB ) ! 911: { ! 912: [abortSRB->srbCmdLock unlockWith: ksrbCmdComplete]; ! 913: abortSRB = (SRB *) NULL; ! 914: SCRIPT_VAR(R_ld_AbortBdr_mailbox) = 0; ! 915: } ! 916: } ! 917: ! 918: /* ! 919: * If the script died, without a vaild nexusIndex, we abort anything that is currently ! 920: * connected and hope for the best! ! 921: */ ! 922: else if ( dstatReg & IID ) ! 923: { ! 924: dspReg = Sym8xxReadRegs( chipBaseAddr, DSP, DSP_SIZE ); ! 925: kprintf("SCSI(Symbios8xx): Illegal script instruction - dsp = %08x srb=0\n\r", dspReg ); ! 926: [self Sym8xxAbortCurrent: (SRB *) -1]; ! 927: } ! 928: ! 929: /* ! 930: * Script signaled conditions ! 931: */ ! 932: else if ( dstatReg & SIR ) ! 933: { ! 934: switch ( dspsReg ) ! 935: { ! 936: case A_abort_current: ! 937: abortCurrentSRB = NULL; ! 938: break; ! 939: ! 940: case A_abort_mailbox: ! 941: [abortSRB->srbCmdLock unlockWith: ksrbCmdComplete]; ! 942: abortSRB = (SRB *) NULL; ! 943: SCRIPT_VAR(R_ld_AbortBdr_mailbox) = 0; ! 944: break; ! 945: ! 946: default: ! 947: [self Sym8xxAbortCurrent: (SRB *)-1]; ! 948: } ! 949: } ! 950: else ! 951: { ! 952: [self Sym8xxAbortCurrent: (SRB *)-1]; ! 953: } ! 954: ! 955: if ( scriptRestartAddr ) ! 956: { ! 957: Sym8xxWriteRegs( chipBaseAddr, DSP, DSP_SIZE, scriptRestartAddr ); ! 958: } ! 959: } ! 960: ! 961: ! 962: /*-----------------------------------------------------------------------------* ! 963: * Abort currently connected target. ! 964: * ! 965: *-----------------------------------------------------------------------------*/ ! 966: - (void) Sym8xxAbortCurrent:(SRB *)srb ! 967: { ! 968: if ( abortCurrentSRB ) ! 969: { ! 970: if ( abortCurrentSRB != srb ) ! 971: { ! 972: // kprintf("SCSI(Symbios8xx): Multiple abort immediate SRBs - resetting\n\r"); ! 973: [self Sym8xxSCSIBusReset: (SRB *)NULL]; ! 974: } ! 975: return; ! 976: } ! 977: ! 978: abortCurrentSRB = srb; ! 979: abortCurrentSRBTimeout = kAbortTimeoutMS / kSCSITimerIntervalMS + 1; ! 980: ! 981: /* ! 982: * Issue abort or abort tag depending on whether the is a tagged request ! 983: */ ! 984: SCRIPT_VAR(R_ld_AbortCode) = EndianSwap32( ((srb != (SRB *)-1) && (srb->nexus.tag >= MIN_SCSI_TAG)) ? 0x0d : 0x06 ); ! 985: scriptRestartAddr = (u_int32_t) &chipRamAddrPhys[Ent_issueAbort_BDR]; ! 986: ! 987: [self Sym8xxClearFifo]; ! 988: } ! 989: ! 990: /*-----------------------------------------------------------------------------* ! 991: * This routine clears the script engine's SCSI and DMA fifos. ! 992: * ! 993: *-----------------------------------------------------------------------------*/ ! 994: - (void) Sym8xxClearFifo ! 995: { ! 996: u_int8_t ctest3Reg; ! 997: u_int8_t stest2Reg; ! 998: u_int8_t stest3Reg; ! 999: ! 1000: stest2Reg = Sym8xxReadRegs( chipBaseAddr, STEST2, STEST2_SIZE ); ! 1001: if ( stest2Reg & ROF ) ! 1002: { ! 1003: Sym8xxWriteRegs( chipBaseAddr, STEST2, STEST2_SIZE, stest2Reg ); ! 1004: } ! 1005: ! 1006: ctest3Reg = Sym8xxReadRegs( chipBaseAddr, CTEST3, CTEST3_SIZE ); ! 1007: ctest3Reg |= CLF; ! 1008: Sym8xxWriteRegs( chipBaseAddr, CTEST3, CTEST3_SIZE, ctest3Reg ); ! 1009: ! 1010: stest3Reg = Sym8xxReadRegs( chipBaseAddr, STEST3, STEST3_SIZE ); ! 1011: stest3Reg |= CSF; ! 1012: Sym8xxWriteRegs( chipBaseAddr,STEST3, STEST3_SIZE, stest3Reg ); ! 1013: ! 1014: do ! 1015: { ! 1016: ctest3Reg = Sym8xxReadRegs( chipBaseAddr, CTEST3, CTEST3_SIZE ); ! 1017: stest2Reg = Sym8xxReadRegs( chipBaseAddr, STEST3, STEST3_SIZE ); ! 1018: stest3Reg = Sym8xxReadRegs( chipBaseAddr, STEST3, STEST3_SIZE ); ! 1019: } ! 1020: while( (ctest3Reg & CLF) || (stest3Reg & CSF) || (stest2Reg & ROF) ); ! 1021: } ! 1022: ! 1023: /*-----------------------------------------------------------------------------* ! 1024: * This routine processes the target's response to our SDTR message. ! 1025: * ! 1026: * We calculate the values for the script engine's timing registers ! 1027: * for synchronous registers, and update our tables indicating that ! 1028: * requested data transfer mode is in-effect. ! 1029: * ! 1030: *-----------------------------------------------------------------------------*/ ! 1031: - (void) Sym8xxNegotiateSDTR:(SRB *) srb Nexus:(Nexus *)nexus ! 1032: { ! 1033: u_int32_t x; ! 1034: u_int8_t *pMsg; ! 1035: u_int32_t syncPeriod; ! 1036: ! 1037: /* ! 1038: * If we were not negotiating, the send MsgReject to targets negotiation ! 1039: * attempt. ! 1040: */ ! 1041: if ( !(srb->srbRequestFlags & ksrbRFNegotiateSync) ) ! 1042: { ! 1043: [self Sym8xxSendMsgReject: srb]; ! 1044: return; ! 1045: } ! 1046: ! 1047: /* ! 1048: * Get pointer to negotiation message received from target. ! 1049: */ ! 1050: pMsg = (u_int8_t *) &SCRIPT_VAR(R_ld_message); ! 1051: ! 1052: /* ! 1053: * The target's SDTR response contains the (transfer period / 4). ! 1054: * ! 1055: * We set our sync clock divisor to 1, 2, or 4 giving us a clock rates ! 1056: * of: ! 1057: * 80Mhz (Period = 12.5ns), ! 1058: * 40Mhz (Period = 25.0ns) ! 1059: * 20Mhz (Period = 50.0ns) ! 1060: * ! 1061: * This is further divided by the value in the sxfer reg to give us the final sync clock rate. ! 1062: * ! 1063: * The requested sync period is scaled up by 1000 and the clock periods are scaled up by 10 ! 1064: * giving a result scaled up by 100. This is rounded-up and converted to sxfer reg values. ! 1065: */ ! 1066: syncPeriod = (u_int32_t)pMsg[3] << 2; ! 1067: if ( syncPeriod < 100 ) ! 1068: { ! 1069: nexus->targetParms.scntl3Reg |= SCNTL3_INIT_875_ULTRA; ! 1070: x = (syncPeriod * 1000) / 125; ! 1071: } ! 1072: else if ( syncPeriod < 200 ) ! 1073: { ! 1074: nexus->targetParms.scntl3Reg |= SCNTL3_INIT_875_FAST; ! 1075: x = (syncPeriod * 1000) / 250; ! 1076: } ! 1077: else ! 1078: { ! 1079: nexus->targetParms.scntl3Reg |= SCNTL3_INIT_875_SLOW; ! 1080: x = (syncPeriod * 1000) / 500; ! 1081: } ! 1082: ! 1083: if ( x % 100 ) x += 100; ! 1084: ! 1085: /* ! 1086: * sxferReg Bits: 5-0 - Transfer offset ! 1087: * 7-6 - Sync Clock Divisor (0 = sync clock / 4) ! 1088: */ ! 1089: nexus->targetParms.sxferReg = ((x/100 - 4) << 5) | pMsg[4]; ! 1090: ! 1091: /* ! 1092: * Update our per-target tables and set-up the hardware regs for this request. ! 1093: * ! 1094: * On reconnection attempts, the script will use our per-target tables to set-up ! 1095: * the scntl3 and sxfer registers in the script engine. ! 1096: */ ! 1097: adapter->targetClocks[srb->target].sxferReg = nexus->targetParms.sxferReg; ! 1098: adapter->targetClocks[srb->target].scntl3Reg = nexus->targetParms.scntl3Reg; ! 1099: ! 1100: Sym8xxWriteRegs( chipBaseAddr, SCNTL3, SCNTL3_SIZE, nexus->targetParms.scntl3Reg ); ! 1101: Sym8xxWriteRegs( chipBaseAddr, SXFER, SXFER_SIZE, nexus->targetParms.sxferReg ); ! 1102: ! 1103: targets[srb->target].flags |= kTFXferSync; ! 1104: ! 1105: scriptRestartAddr = (u_int32_t) &chipRamAddrPhys[Ent_clearACK]; ! 1106: } ! 1107: ! 1108: /*-----------------------------------------------------------------------------* ! 1109: * This routine processes the target's response to our WDTR message. ! 1110: * ! 1111: * In addition, if there is a pending SDTR message, this routine sends it ! 1112: * to the target. ! 1113: * ! 1114: *-----------------------------------------------------------------------------*/ ! 1115: - (void) Sym8xxNegotiateWDTR:(SRB *) srb Nexus:(Nexus *)nexus ! 1116: { ! 1117: u_int8_t *pMsg; ! 1118: u_int32_t msgBytesSent; ! 1119: u_int32_t msgBytesLeft; ! 1120: ! 1121: /* ! 1122: * If we were not negotiating, the send MsgReject to targets negotiation ! 1123: * attempt. ! 1124: */ ! 1125: if ( !(srb->srbRequestFlags & ksrbRFNegotiateWide) ) ! 1126: { ! 1127: [self Sym8xxSendMsgReject: srb]; ! 1128: return; ! 1129: } ! 1130: ! 1131: /* ! 1132: * Set Wide (16-bit) vs Narrow (8-bit) data transfer mode based on target's response. ! 1133: */ ! 1134: pMsg = (u_int8_t *) &SCRIPT_VAR(R_ld_message); ! 1135: ! 1136: if ( pMsg[3] == 1 ) ! 1137: { ! 1138: nexus->targetParms.scntl3Reg |= EWS; ! 1139: } ! 1140: else ! 1141: { ! 1142: nexus->targetParms.scntl3Reg &= ~EWS; ! 1143: } ! 1144: ! 1145: /* ! 1146: * Update our per-target tables and set-up the hardware regs for this request. ! 1147: * ! 1148: * On reconnection attempts, the script will use our per-target tables to set-up ! 1149: * the scntl3 and sxfer registers in the script engine. ! 1150: */ ! 1151: ! 1152: adapter->targetClocks[srb->target].scntl3Reg = nexus->targetParms.scntl3Reg; ! 1153: Sym8xxWriteRegs( chipBaseAddr, SCNTL3, SCNTL3_SIZE, nexus->targetParms.scntl3Reg ); ! 1154: ! 1155: targets[srb->target].flags |= kTFXferWide16; ! 1156: ! 1157: /* ! 1158: * If there any pending messages left for the target, send them now, ! 1159: */ ! 1160: msgBytesSent = EndianSwap32( nexus->msg.length ); ! 1161: msgBytesLeft = srb->srbMsgLength - msgBytesSent; ! 1162: if ( msgBytesLeft ) ! 1163: { ! 1164: nexus->msg.length = EndianSwap32( msgBytesLeft ); ! 1165: nexus->msg.ppData = EndianSwap32( EndianSwap32( nexus->msg.ppData ) + msgBytesSent ); ! 1166: scriptRestartAddr = (u_int32_t) &chipRamAddrPhys[Ent_issueMessageOut]; ! 1167: } ! 1168: ! 1169: /* ! 1170: * Otherwise, tell the script we're done with MsgOut phase. ! 1171: */ ! 1172: else ! 1173: { ! 1174: scriptRestartAddr = (u_int32_t) &chipRamAddrPhys[Ent_clearACK]; ! 1175: } ! 1176: } ! 1177: ! 1178: /*-----------------------------------------------------------------------------* ! 1179: * Reject message received from target. ! 1180: * ! 1181: *-----------------------------------------------------------------------------*/ ! 1182: - (void) Sym8xxSendMsgReject:(SRB *) srb ! 1183: { ! 1184: srb->nexus.msg.ppData = EndianSwap32((u_int32_t)&srb->srbPhys->nexus.msgData); ! 1185: srb->nexus.msg.length = EndianSwap32(0x01); ! 1186: srb->nexus.msgData[0] = 0x07; ! 1187: ! 1188: scriptRestartAddr = (u_int32_t) &chipRamAddrPhys[Ent_issueMessageOut]; ! 1189: } ! 1190: ! 1191: /*-----------------------------------------------------------------------------* ! 1192: * This routine snoops inquiry data. ! 1193: * ! 1194: * The indirect SCSI Disk driver in driverKit, does not bother to check the target's ! 1195: * capabilities before enabling Synchronous Negotiation or Cmd Queueing. If things ! 1196: * were left to themselves, targets that did not support tags would be broken since ! 1197: * the request comming from driverKit always indicate that tags are allowed. ! 1198: * ! 1199: *-----------------------------------------------------------------------------*/ ! 1200: - (void) Sym8xxCheckInquiryData: (SRB *)srb ! 1201: { ! 1202: IOMemoryDescriptor *mem; ! 1203: inquiry_reply_t inqData; ! 1204: u_int32_t inqSize; ! 1205: ! 1206: bzero( &inqData, sizeof(inqData) ); ! 1207: ! 1208: inqSize = (srb->xferDone < sizeof(inqData)) ? srb->xferDone : sizeof(inqData); ! 1209: ! 1210: mem = [[ IOSimpleMemoryDescriptor alloc ] initWithAddress: (void *)srb->xferBuffer length: inqSize ]; ! 1211: [mem setClient: srb->xferClient]; ! 1212: ! 1213: do ! 1214: { ! 1215: if ( srb->srbSCSIResult != SR_IOST_GOOD ) ! 1216: { ! 1217: continue; ! 1218: } ! 1219: if ( srb->xferDone < offsetof(inquiry_reply_t, ir_vendorid) ) ! 1220: { ! 1221: continue; ! 1222: } ! 1223: ! 1224: if ( [mem readFromClient: (void *) &inqData count: inqSize] != inqSize ) ! 1225: { ! 1226: continue; ! 1227: } ! 1228: if ( inqData.ir_qual != DEVQUAL_OK ) ! 1229: { ! 1230: continue; ! 1231: } ! 1232: ! 1233: if ( inqData.ir_wbus16 ) ! 1234: { ! 1235: targets[srb->target].flags |= kTFXferWide16Supported; ! 1236: } ! 1237: if ( inqData.ir_sync ) ! 1238: { ! 1239: targets[srb->target].flags |= kTFXferSyncSupported; ! 1240: } ! 1241: if ( inqData.ir_cmdque ) ! 1242: { ! 1243: targets[srb->target].flags |= kTFCmdQueueSupported; ! 1244: } ! 1245: } ! 1246: while ( 0 ); ! 1247: [mem release]; ! 1248: } ! 1249: ! 1250: ! 1251: /*-----------------------------------------------------------------------------* ! 1252: * This routine initiates a SCSI Bus Reset. ! 1253: * ! 1254: * This may be an internally generated request as part of error recovery or ! 1255: * a client's bus reset request. ! 1256: * ! 1257: *-----------------------------------------------------------------------------*/ ! 1258: - (void) Sym8xxSCSIBusReset: (SRB *)srb ! 1259: { ! 1260: if ( srb ) ! 1261: { ! 1262: if ( resetSRB ) ! 1263: { ! 1264: srb->srbSCSIResult = SR_IOST_CMDREJ; ! 1265: [srb->srbCmdLock unlockWith: ksrbCmdComplete]; ! 1266: return; ! 1267: } ! 1268: resetSRB = srb; ! 1269: } ! 1270: ! 1271: Sym8xxWriteRegs( chipBaseAddr, SCNTL1, SCNTL1_SIZE, SCNTL1_SCSI_RST ); ! 1272: IODelay( 25 ); ! 1273: Sym8xxWriteRegs( chipBaseAddr, SCNTL1, SCNTL1_SIZE, SCNTL1_INIT ); ! 1274: } ! 1275: ! 1276: /*-----------------------------------------------------------------------------* ! 1277: * This routine handles a SCSI Bus Reset interrupt. ! 1278: * ! 1279: * The SCSI Bus reset may be generated by a target on the bus, internally from ! 1280: * the driver's error recovery or from a client request. ! 1281: * ! 1282: * Once the reset is detected we establish a settle period where new client requests ! 1283: * are blocked in the client thread. In addition we flush all currently executing ! 1284: * scsi requests back to the client. ! 1285: * ! 1286: *-----------------------------------------------------------------------------*/ ! 1287: - (void) Sym8xxProcessSCSIBusReset ! 1288: { ! 1289: SRB *srb = 0; ! 1290: Nexus *nexus = 0; ! 1291: u_int32_t i; ! 1292: ! 1293: /* ! 1294: * If we got another bus reset event during the settle period we extend the settle ! 1295: * period accordingly. ! 1296: */ ! 1297: if ( resetQuiesceTimer ) ! 1298: { ! 1299: resetQuiesceTimer = kResetQuiesceDelayMS / kSCSITimerIntervalMS + 1; ! 1300: return; ! 1301: } ! 1302: ! 1303: resetSeqNum = srbSeqNum; ! 1304: // kprintf("SCSI(Symbios8xx): Reset Started - SRB Seq = %d\n\r", resetSeqNum); ! 1305: ! 1306: /* ! 1307: * We take the resetQuiesceSem lock which will block new client thread requests. ! 1308: * ! 1309: * Note: The client thread checks resetQuiesceTimer for <> 0 before taking this lock. ! 1310: */ ! 1311: [resetQuiesceSem lock]; ! 1312: resetQuiesceTimer = kResetQuiesceDelayMS / kSCSITimerIntervalMS + 1; ! 1313: ! 1314: /* ! 1315: * We end any aborts currently in progress ! 1316: */ ! 1317: abortCurrentSRB = (SRB *)NULL; ! 1318: ! 1319: if ( abortSRB ) ! 1320: { ! 1321: [abortSRB->srbCmdLock unlockWith: ksrbCmdComplete]; ! 1322: abortSRB = (SRB *) NULL; ! 1323: } ! 1324: ! 1325: [self Sym8xxClearFifo]; ! 1326: ! 1327: /* ! 1328: * We return anything in our Nexus table back to the client ! 1329: */ ! 1330: for ( i=0; i < MAX_SCSI_TAG; i++ ) ! 1331: { ! 1332: nexus = adapter->nexusPtrsVirt[i]; ! 1333: if ( nexus == (Nexus *) -1 ) ! 1334: { ! 1335: continue; ! 1336: } ! 1337: ! 1338: srb = (SRB *)((u_int32_t)nexus - offsetof(SRB, nexus)); ! 1339: ! 1340: srb->srbSCSIResult = SR_IOST_RESET; ! 1341: ! 1342: adapter->nexusPtrsVirt[i] = (Nexus *) -1; ! 1343: adapter->nexusPtrsPhys[i] = (Nexus *) -1; ! 1344: ! 1345: [srb->srbCmdLock unlockWith: ksrbCmdComplete]; ! 1346: } ! 1347: ! 1348: /* ! 1349: * We clear the script's request mailboxes. Any work in the script mailboxes is ! 1350: * already in the NexusPtr tables so we have already have handled the SRB/Nexus ! 1351: * cleanup. ! 1352: */ ! 1353: for ( i=0; i < MAX_SCHED_MAILBOXES; i++ ) ! 1354: { ! 1355: adapter->schedMailBox[i] = 0; ! 1356: } ! 1357: ! 1358: SCRIPT_VAR(R_ld_AbortBdr_mailbox) = 0; ! 1359: SCRIPT_VAR(R_ld_IOdone_mailbox) = 0; ! 1360: SCRIPT_VAR(R_ld_counter) = 0; ! 1361: mailBoxIndex = 0; ! 1362: ! 1363: /* ! 1364: * Reset the data transfer mode/clocks in our per-target tables back to Async/Narrow 8-bit ! 1365: */ ! 1366: for ( i=0; i < MAX_SCSI_TARGETS; i++ ) ! 1367: { ! 1368: targets[i].flags &= ~(kTFXferSync | kTFXferWide16); ! 1369: ! 1370: adapter->targetClocks[i].scntl3Reg = SCNTL3_INIT_875; ! 1371: adapter->targetClocks[i].sxferReg = 0; ! 1372: } ! 1373: ! 1374: scriptRestartAddr = (u_int32_t) &chipRamAddrPhys[Ent_select_phase]; ! 1375: Sym8xxWriteRegs( chipBaseAddr, DSP, DSP_SIZE, scriptRestartAddr ); ! 1376: ! 1377: } ! 1378: ! 1379: /*-----------------------------------------------------------------------------* ! 1380: * This routine sets the SIGP bit in the script engine's ISTAT ! 1381: * register. This signals the script to wake-up for a WAIT for ! 1382: * reselection instruction. The script will then check the mailboxes ! 1383: * for work to do. ! 1384: * ! 1385: *-----------------------------------------------------------------------------*/ ! 1386: - (void) Sym8xxSignalScript:(SRB *)srb ! 1387: { ! 1388: Sym8xxWriteRegs( chipBaseAddr, ISTAT, ISTAT_SIZE, SIGP ); ! 1389: } ! 1390: ! 1391: /*-----------------------------------------------------------------------------* ! 1392: * Timeout handler. ! 1393: * ! 1394: * This routine is scheduled and implements timeouts for the driver. ! 1395: * ! 1396: * The following items are timed: ! 1397: * ! 1398: * - Reset settle period. ! 1399: * - Aborts ! 1400: * - SRBs ! 1401: * ! 1402: *-----------------------------------------------------------------------------*/ ! 1403: - (void) timeoutOccurred ! 1404: { ! 1405: SRB *srb; ! 1406: Nexus *nexus; ! 1407: u_int32_t i; ! 1408: u_int32_t nexusIndex; ! 1409: u_int32_t mailboxNexusIndex = -1; ! 1410: ! 1411: if ( SCRIPT_VAR( R_ld_IOdone_mailbox ) ) ! 1412: { ! 1413: mailboxNexusIndex = ((IODoneMailBox *)&SCRIPT_VAR(R_ld_IOdone_mailbox))->nexus; ! 1414: } ! 1415: ! 1416: nexusIndex = EndianSwap32( SCRIPT_VAR(R_ld_nexus_index) ); ! 1417: ! 1418: /* ! 1419: * If we are in a reset settle period, suspend all other timing. ! 1420: * ! 1421: * When the reset settle period completes, return the SRB if the ! 1422: * client requested the bus reset. Also unlock the reset semaphore. ! 1423: */ ! 1424: if ( resetQuiesceTimer ) ! 1425: { ! 1426: if ( --resetQuiesceTimer ) ! 1427: { ! 1428: goto timeoutOccurred_Exit; ! 1429: } ! 1430: ! 1431: if ( resetSRB ) ! 1432: { ! 1433: [resetSRB->srbCmdLock unlockWith: ksrbCmdComplete]; ! 1434: resetSRB = (SRB *) NULL; ! 1435: } ! 1436: // kprintf("SCSI(Symbios8xx): Reset Ended - SRB Seq = %d\n\r", srbSeqNum); ! 1437: [resetQuiesceSem unlock]; ! 1438: } ! 1439: ! 1440: /* ! 1441: * Check whether an abort timed out. If it does, then its likely that a target is ! 1442: * hung on the bus. In this case the only recourse is to issue a SCSI Bus reset. ! 1443: */ ! 1444: if ( abortCurrentSRB && abortCurrentSRBTimeout ) ! 1445: { ! 1446: if ( !(--abortCurrentSRBTimeout) ) ! 1447: { ! 1448: // kprintf("SCSI(Symbios8xx): Abort Current SRB failed - resetting\n\r"); ! 1449: [self Sym8xxSCSIBusReset: (SRB *)NULL]; ! 1450: goto timeoutOccurred_Exit; ! 1451: } ! 1452: } ! 1453: ! 1454: if ( abortSRB && abortSRBTimeout ) ! 1455: { ! 1456: if ( !(--abortSRBTimeout) ) ! 1457: { ! 1458: // kprintf("SCSI(Symbios8xx): MailBox abort failed - resetting\n\r"); ! 1459: [self Sym8xxSCSIBusReset: (SRB *)NULL]; ! 1460: goto timeoutOccurred_Exit; ! 1461: } ! 1462: } ! 1463: ! 1464: /* ! 1465: * Scan the Nexus pointer table looking for SRBs to timeout ! 1466: */ ! 1467: for ( i=0; i < MAX_SCSI_TAG; i++ ) ! 1468: { ! 1469: nexus = adapter->nexusPtrsVirt[i]; ! 1470: if ( nexus == (Nexus *) -1 ) ! 1471: { ! 1472: continue; ! 1473: } ! 1474: ! 1475: srb = (SRB *)((u_int32_t)nexus - offsetof(SRB, nexus)); ! 1476: if ( srb->srbTimeout ) ! 1477: { ! 1478: if ( !(--srb->srbTimeout) ) ! 1479: { ! 1480: // kprintf("SCSI(Symbios8xx): Timeout - Target = %d SRB = %08x SRB Seq = %d\n\r", ! 1481: // srb->target, (u_int32_t)srb, srb->srbSeqNum ); ! 1482: ! 1483: /* If the SRB we're timing out is in the script's IODone mailbox, then ! 1484: * clear the mailbox in addition to timing out the request. ! 1485: */ ! 1486: if ( i == mailboxNexusIndex ) ! 1487: { ! 1488: SCRIPT_VAR(R_ld_IOdone_mailbox) = 0; ! 1489: } ! 1490: ! 1491: /* ! 1492: * If the target for the SRB we're timing out is currently connected on ! 1493: * the SCSI Bus, then issue an abort. Since it is likely that the script ! 1494: * is running in this scenario, we call a Sym8xxAbortScript to shutdown the ! 1495: * script engine in an orderly fashion. ! 1496: */ ! 1497: if ( i == nexusIndex ) ! 1498: { ! 1499: [self Sym8xxAbortScript]; ! 1500: srb->srbSCSIResult = SR_IOST_IOTO; ! 1501: [self Sym8xxAbortCurrent: srb]; ! 1502: Sym8xxWriteRegs( chipBaseAddr, DSP, DSP_SIZE, scriptRestartAddr ); ! 1503: } ! 1504: ! 1505: /* ! 1506: * If target for the SRB we're timing out is not on the SCSI bus, then ! 1507: * we mark the request as requiring an Abort and return it to the client ! 1508: * thread. The client thread will then schedule the abort. ! 1509: */ ! 1510: else ! 1511: { ! 1512: adapter->nexusPtrsVirt[i] = (Nexus *) -1; ! 1513: adapter->nexusPtrsPhys[i] = (Nexus *) -1; ! 1514: ! 1515: srb->srbSCSIResult = SR_IOST_IOTO; ! 1516: srb->srbCmd = ksrbCmdProcessTimeout; ! 1517: [srb->srbCmdLock unlockWith: ksrbCmdComplete]; ! 1518: } ! 1519: } ! 1520: } ! 1521: } ! 1522: ! 1523: ! 1524: timeoutOccurred_Exit: ; ! 1525: /* ! 1526: * Reschedule the next timer interval. ! 1527: */ ! 1528: ns_timeout((func) Sym8xxTimerReq, (void *) self, (ns_time_t) kSCSITimerIntervalMS * 1000 * 1000, (int)CALLOUT_PRI_THREAD); ! 1529: } ! 1530: ! 1531: /*-----------------------------------------------------------------------------* ! 1532: * This routine does a mailbox abort. ! 1533: * ! 1534: * This type of abort is used for targets not currently connected to the SCSI Bus. ! 1535: * ! 1536: * The script will select the target and send a tag (if required) followed by the ! 1537: * appropriate abort message (abort/abort-tag) ! 1538: * ! 1539: *-----------------------------------------------------------------------------*/ ! 1540: - (void) Sym8xxAbortBdr:(SRB *) srb ! 1541: { ! 1542: IOAbortBdrMailBox abortMailBox; ! 1543: ! 1544: abortSRB = srb; ! 1545: abortSRBTimeout = kAbortTimeoutMS / kSCSITimerIntervalMS + 1; ! 1546: ! 1547: /* ! 1548: * Setup a script variable containing the abort information. ! 1549: */ ! 1550: abortMailBox.message = ( srb->nexus.tag < MIN_SCSI_TAG) ? 0x06 : 0x0d; ! 1551: abortMailBox.identify = srb->lun | 0xC0 ; ! 1552: abortMailBox.scsi_id = srb->target; ! 1553: abortMailBox.tag = ( srb->nexus.tag < MIN_SCSI_TAG) ? 0 : srb->nexus.tag; ! 1554: ! 1555: SCRIPT_VAR(R_ld_AbortBdr_mailbox) = *(u_int32_t *) &abortMailBox; ! 1556: ! 1557: [self Sym8xxSignalScript: srb]; ! 1558: } ! 1559: ! 1560: /*-----------------------------------------------------------------------------* ! 1561: * This routine is used to shutdown the script engine in an orderly fashion. ! 1562: * ! 1563: * Normally the script engine automatically stops when an interrupt is generated. However, ! 1564: * in the case of timeouts we need to change the script engine's dsp reg (instruction pointer). ! 1565: * to issue an abort. ! 1566: * ! 1567: *-----------------------------------------------------------------------------*/ ! 1568: - (void) Sym8xxAbortScript ! 1569: { ! 1570: ns_time_t currentTime; ! 1571: ns_time_t endTime; ! 1572: ! 1573: [self disableAllInterrupts]; ! 1574: ! 1575: /* ! 1576: * We set the ABRT bit in ISTAT and spin until the script engine acknowledges the ! 1577: * abort or we timeout. ! 1578: */ ! 1579: Sym8xxWriteRegs( chipBaseAddr, ISTAT, ISTAT_SIZE, ABRT ); ! 1580: ! 1581: IOGetTimestamp( &endTime ); ! 1582: ! 1583: endTime += (kAbortScriptTimeoutMS * 1000 * 1000); ! 1584: ! 1585: do ! 1586: { ! 1587: IOGetTimestamp( ¤tTime ); ! 1588: ! 1589: istatReg = Sym8xxReadRegs( chipBaseAddr, ISTAT, ISTAT_SIZE ); ! 1590: ! 1591: if ( istatReg & SIP ) ! 1592: { ! 1593: Sym8xxReadRegs( chipBaseAddr, SIST, SIST_SIZE ); ! 1594: continue; ! 1595: } ! 1596: ! 1597: if ( istatReg & DIP ) ! 1598: { ! 1599: Sym8xxWriteRegs( chipBaseAddr, ISTAT, ISTAT_SIZE, 0x00 ); ! 1600: Sym8xxReadRegs( chipBaseAddr, DSTAT, DSTAT_SIZE ); ! 1601: break; ! 1602: } ! 1603: } ! 1604: while ( currentTime < endTime ); ! 1605: ! 1606: istatReg = SIGP; ! 1607: Sym8xxWriteRegs( chipBaseAddr, ISTAT, ISTAT_SIZE, istatReg ); ! 1608: ! 1609: [self enableAllInterrupts]; ! 1610: ! 1611: if ( currentTime >= endTime ) ! 1612: { ! 1613: // kprintf( "SCSI(Symbios8xx): Abort script failed - resetting bus\n\r" ); ! 1614: [self Sym8xxSCSIBusReset: NULL]; ! 1615: } ! 1616: ! 1617: } ! 1618: ! 1619: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.