|
|
1.1 root 1: /* Copyright (c) 1994 NeXT Computer, Inc. All rights reserved.
2: *
3: * AMD_Chip.m - chip (53C974/79C974) specific methods for AMD SCSI driver
4: *
5: * HISTORY
6: * 21 Oct 94 Doug Mitchell at NeXT
7: * Created.
8: */
9:
10: #import "AMD_Chip.h"
11: #import "AMD_ChipPrivate.h"
12: #import "AMD_Private.h"
13: #import "AMD_x86.h"
14: #import "AMD_Regs.h"
15: #import "AMD_Types.h"
16: #import "AMD_ddm.h"
17: #import "bringup.h"
18: #import <driverkit/generalFuncs.h>
19: #import <kernserv/prototypes.h>
20:
21: IONamedValue scsiMsgValues[] = {
22: {MSG_CMDCMPLT, "Command Complete" },
23: {MSG_EXTENDED, "Extended Message" },
24: {MSG_SAVEPTRS, "Save Pointers" },
25: {MSG_RESTOREPTRS, "Restore Pointers" },
26: {MSG_DISCONNECT, "Disconnect" },
27: {MSG_IDETERR, "Initiator Det Error" },
28: {MSG_ABORT, "Abort" },
29: {MSG_MSGREJECT, "Message Reject" },
30: {MSG_NOP, "Nop" },
31: {MSG_MSGPARERR, "Message parity Error" },
32: {0, NULL }
33: };
34:
35: #ifdef DDM_DEBUG
36: IONamedValue scsiPhaseValues[] = {
37: {PHASE_DATAOUT, "data_out" },
38: {PHASE_DATAIN, "data_in" },
39: {PHASE_COMMAND, "command" },
40: {PHASE_STATUS, "status" },
41: {PHASE_MSGOUT, "message_out" },
42: {PHASE_MSGIN, "message_in" },
43: {0, NULL }
44: };
45:
46: #endif DDM_DEBUG
47:
48: #ifdef DEBUG
49: /*
50: * For IOFindNameForValue() and ddm's.
51: */
52: IONamedValue scStateValues[] = {
53: {SCS_UNINITIALIZED, "SCS_UNINITIALIZED" },
54: {SCS_DISCONNECTED, "SCS_DISCONNECTED" },
55: {SCS_SELECTING, "SCS_SELECTING" },
56: {SCS_INITIATOR, "SCS_INITIATOR" },
57: {SCS_COMPLETING, "SCS_COMPLETING" },
58: {SCS_DMAING, "SCS_DMAING" },
59: {SCS_ACCEPTINGMSG, "SCS_ACCEPTINGMSG" },
60: {SCS_SENDINGMSG, "SCS_SENDINGMSG" },
61: {SCS_GETTINGMSG, "SCS_GETTINGMSG" },
62: {SCS_SENDINGCMD, "SCS_SENDINGCMD" },
63: {0, NULL },
64: };
65: #endif DEBUG
66:
67: @implementation AMD_SCSI(Chip)
68:
69: /*
70: * One-time-only init and probe. Returns YES if a functioning chip is
71: * found, else returns NO. -hwReset must be called subsequent to this
72: * to enable operation of the chip.
73: */
74: - (BOOL)probeChip
75: {
76: int target;
77:
78: /*
79: * Init sync mode to async, until we negotiate.
80: */
81: for(target=0; target<SCSI_NTARGETS; target++) {
82: perTarget[target].syncXferOffset = 0;
83: }
84: return YES;
85: }
86:
87: /*
88: * Reusable 53C974 init function. This includes a SCSI reset.
89: * Handling of ioComplete of active and disconnected commands must be done
90: * elsewhere. Returns non-zero on error.
91: */
92: - (int)hwReset : (const char *)reason
93: {
94: int target;
95: unsigned char reg;
96:
97: /*
98: * First of all, reset interrupts, the SCSI block, and the DMA engine.
99: */
100: [self disableAllInterrupts];
101: WRITE_REG(scsiCmd, SCMD_RESET_DEVICE);
102: WRITE_REG(scsiCmd, SCMD_NOP);
103: [self dmaIdle];
104:
105: /*
106: * Clear possible pending interrupt.
107: */
108: READ_REG(intrStatus);
109:
110: /*
111: * Init state variables.
112: */
113: reselPending = 0;
114: scState = SCS_DISCONNECTED;
115: activeCmd = NULL;
116: currMsgInCnt = 0;
117: currMsgOutCnt = 0;
118: msgOutState = MOS_NONE;
119: SDTR_State = SNS_NONE;
120:
121: /*
122: * Sync negotiation is needed after a reset.
123: */
124: for(target=0; target<SCSI_NTARGETS; target++) {
125: perTarget[target].syncNegotNeeded = 1;
126: }
127:
128: /*
129: * Control1....
130: */
131: reg = CR1_RESET_INTR_DIS | CR1_PERR_ENABLE | AMD_SCSI_ID;
132: if(extendTiming) {
133: /*
134: * Per instance table. This slows down transfers on the
135: * bus.
136: */
137: reg |= CR1_EXTEND_TIMING;
138: }
139: WRITE_REG(control1, reg);
140: ddm_init("control1 = 0x%x\n", reg, 2,3,4,5);
141: hostId = AMD_SCSI_ID;
142:
143: /*
144: * Clock factor and select timeout.
145: */
146: ASSERT(scsiClockRate != 0);
147: if(scsiClockRate < 10) {
148: IOLog("AMD53C974: Clock %d MHZ too low; using 10 MHz\n",
149: scsiClockRate);
150: scsiClockRate = 10;
151: }
152: if(scsiClockRate > 40) {
153: IOLog("AMD53C974: Clock %d MHZ too high; using 40 MHz\n",
154: scsiClockRate);
155: scsiClockRate = 40;
156: }
157: reg = AMD_CLOCK_FACTOR(scsiClockRate) & 0x7;
158: WRITE_REG(clockFactor, reg);
159: ddm_init("clockFactor %d\n", reg, 2,3,4,5);
160: reg = amdSelectTimeout(AMD_SELECT_TO, scsiClockRate);
161: WRITE_REG(scsiTimeout, reg);
162: ddm_init("select timeout reg 0x%x\n", reg, 2,3,4,5);
163:
164: /*
165: * control2 - enable extended features - mainly, 24-bit transfer count.
166: */
167: WRITE_REG(control2, CR2_ENABLE_FEAT);
168:
169: /*
170: * control3
171: */
172: reg = 0;
173: if(fastModeEnable) {
174: reg |= CR3_FAST_SCSI;
175: }
176: if(scsiClockRate > 25) {
177: reg |= CR3_FAST_CLOCK;
178: }
179: ddm_init("control3 = 0x%x\n", reg, 2,3,4,5);
180: WRITE_REG(control3, reg);
181:
182: /*
183: * control4 - glitch eater, active negation. Let's not
184: * worry about these whizzy features just yet.
185: */
186: WRITE_REG(control4, 0);
187:
188: /*
189: * Go to async xfer mode for now. Sync gets enabled on a per-target
190: * basis in -targetContext.
191: */
192: WRITE_REG(syncOffset, 0);
193:
194: /*
195: * Reset SCSI bus, wait, clear possible interrupt.
196: */
197: WRITE_REG(scsiCmd, SCMD_RESET_SCSI);
198: if(reason) {
199: IOLog("AMD53C974: Resetting SCSI bus (%s)\n", reason);
200: }
201: else {
202: IOLog("AMD53C974: Resetting SCSI bus\n");
203: }
204: IOSleep(AMD_SCSI_RESET_DELAY);
205: READ_REG(intrStatus);
206:
207: ddm_init("hwReset: enabling interrupts\n", 1,2,3,4,5);
208: [self enableAllInterrupts];
209:
210: ddm_init("hwReset: DONE\n", 1,2,3,4,5);
211: return 0;
212: }
213:
214: /*
215: * reset SCSI bus.
216: */
217: - (void)scsiReset
218: {
219: WRITE_REG(scsiCmd, SCMD_RESET_SCSI);
220: READ_REG(intrStatus);
221: }
222:
223: /*
224: * Start a SCSI transaction for the specified command. ActiveCmd must be
225: * NULL. A return of HWS_REJECT indicates that caller may try again
226: * with another command; HWS_BUSY indicates a condition other than
227: * (activeCmd != NULL) which prevents the processing of the command.
228: */
229: - (hwStartReturn)hwStart : (commandBuf *)cmdBuf
230: {
231: unsigned char cdb_ctrl;
232: IOSCSIRequest *scsiReq = cmdBuf->scsiReq;
233: cdb_t *cdbp = &scsiReq->cdb;
234: unsigned char identify_msg = 0;
235: unsigned char *cp;
236: unsigned char okToDisc;
237: unsigned char okToQueue;
238: perTargetData *perTargetPtr;
239: int i;
240: BOOL cmdQueueDisableFlag = NO;
241: unsigned char selectCmd;
242:
243: ddm_chip("hwStart cmdBuf = 0x%x opcode %s\n", cmdBuf,
244: IOFindNameForValue(cdbp->cdb_opcode, IOSCSIOpcodeStrings),
245: 3,4,5);
246: ASSERT(activeCmd == NULL);
247:
248: /*
249: * Currently, the only reason we return HWS_BUSY is if we have
250: * a reselect pending.
251: */
252: if(reselPending) {
253: queue_enter(&pendingQ, cmdBuf, commandBuf *, link);
254: return HWS_BUSY;
255: }
256: ASSERT(scState == SCS_DISCONNECTED);
257:
258: /*
259: * Initialize driver return values and state machine.
260: */
261: cmdBuf->currentByteCount = cmdBuf->savedByteCount =
262: scsiReq->maxTransfer;
263: scsiReq->bytesTransferred = 0;
264: cmdBuf->savedPtr = cmdBuf->currentPtr = (vm_offset_t)cmdBuf->buffer;
265: scsiReq->driverStatus = SR_IOST_INVALID;
266: scsiReq->totalTime = 0ULL;
267: scsiReq->latentTime = 0ULL;
268:
269: /*
270: * Figure out what kind of cdb we've been given and grab the ctrl byte.
271: */
272: switch (SCSI_OPGROUP(cdbp->cdb_opcode)) {
273: case OPGROUP_0:
274: cmdBuf->cdbLength = sizeof(cdb_6_t);
275: cdb_ctrl = cdbp->cdb_c6.c6_ctrl;
276: break;
277: case OPGROUP_1:
278: case OPGROUP_2:
279: cmdBuf->cdbLength = sizeof(cdb_10_t);
280: cdb_ctrl = cdbp->cdb_c10.c10_ctrl;
281: break;
282: case OPGROUP_5:
283: cmdBuf->cdbLength = sizeof(cdb_12_t);
284: cdb_ctrl = cdbp->cdb_c12.c12_ctrl;
285: break;
286: case OPGROUP_6:
287: cmdBuf->cdbLength = (scsiReq->cdbLength ?
288: scsiReq->cdbLength : sizeof (struct cdb_6));
289: cdb_ctrl = 0;
290: break;
291: case OPGROUP_7:
292: cmdBuf->cdbLength = (scsiReq->cdbLength ?
293: scsiReq->cdbLength : sizeof (struct cdb_10));
294: cdb_ctrl = 0;
295: break;
296: default:
297: goto abortReq;
298: }
299: ddm_chip("cdbLength = %d\n", cmdBuf->cdbLength, 2,3,4,5);
300:
301: /*
302: * Do a little command snooping.
303: */
304: perTargetPtr = &perTarget[scsiReq->target];
305: switch(cdbp->cdb_opcode) {
306: case C6OP_INQUIRY:
307: /*
308: * The first command SCSIDisk sends us is an Inquiry command.
309: * This never gets retried, so avoid a possible
310: * reject of a command queue tag. Avoid this hack if
311: * there are any other commands outstanding for this
312: * target/lun.
313: */
314: if(activeArray[scsiReq->target][scsiReq->lun] == 0) {
315: cmdQueueDisableFlag = YES;
316: }
317: break;
318:
319: case C6OP_REQSENSE:
320: /*
321: * Always force sync renegotiation on this one to
322: * catch independent target power cycles.
323: */
324: if(SYNC_RENEGOT_ON_REQ_SENSE) {
325: perTargetPtr->syncNegotNeeded = 1;
326: }
327: break;
328: }
329:
330: /*
331: * Avoid command queueing if if we're going to do sync
332: * negotiation.
333: * FIXME - this might be illegal - what if we're doing a request
334: * sense in response to a legitimate error, and there are
335: * tagged commands pending?
336: */
337: if(perTargetPtr->syncNegotNeeded &&
338: !perTargetPtr->syncDisable &&
339: syncModeEnable) {
340: cmdQueueDisableFlag = YES;
341: SDTR_State = SNS_HOST_INIT_NEEDED;
342: ddm_chip("hwStart: entering SNS_HOST_INIT_NEEDED state\n",
343: 1,2,3,4,5);
344: }
345: else {
346: SDTR_State = SNS_NONE;
347: }
348:
349: /*
350: * Determine from myriad sources whether or not it's OK to
351: * disconnect and to use command queueing.
352: */
353: okToQueue = cmdQueueEnable && // global per driver
354: !scsiReq->cmdQueueDisable && // per I/O
355: !perTargetPtr->cmdQueueDisable && // per target
356: !cmdQueueDisableFlag; // inquiry hack
357: okToDisc = ([self numReserved] > // > 1 target on bus
358: (1 + SCSI_NLUNS)) ||
359: okToQueue; // hope to do cmd q'ing
360: #if FORCE_DISCONNECTS
361: okToDisc = 1;
362: #else FORCE_DISCONNECTS
363: if(!scsiReq->disconnect) {
364: /*
365: * This overrides everything...
366: */
367: okToQueue = okToDisc = 0;
368: }
369: #endif FORCE_DISCONNECTS
370: cmdBuf->discEnable = okToDisc;
371: if(okToQueue) {
372: /*
373: * Avoid using tag QUEUE_TAG_NONTAGGED...
374: */
375: cmdBuf->queueTag = nextQueueTag;
376: if(++nextQueueTag == QUEUE_TAG_NONTAGGED) {
377: nextQueueTag++;
378: }
379: }
380: else {
381: cmdBuf->queueTag = QUEUE_TAG_NONTAGGED;
382: }
383:
384: /*
385: * Make sure nothing unreasonable has been asked of us.
386: */
387: if((cdb_ctrl & CTRL_LINKFLAG) != CTRL_NOLINK) {
388: ddm_err("Linked CDB (Unimplemented)\n",
389: 1,2,3,4,5);
390: goto abortReq;
391: }
392:
393: /*
394: * OK, this command is hot.
395: */
396: [self activateCommand:cmdBuf];
397:
398: scState = SCS_SELECTING;
399: msgOutState = MOS_NONE;
400: bzero(currMsgIn, AMD_MSG_SIZE);
401: bzero(currMsgOut, AMD_MSG_SIZE);
402: currMsgInCnt = 0;
403: currMsgOutCnt = 0;
404:
405: /*
406: * Load per-target context.
407: */
408: [self targetContext:scsiReq->target];
409:
410: /*
411: * set target bus id
412: * punch message(s), optional cdb into fifo
413: * write appropriate select command
414: */
415: ddm_chip("hwStart: opcode 0x%x targ %d lun %d maxTransfer 0x%x\n",
416: cdbp->cdb_opcode, scsiReq->target, scsiReq->lun,
417: scsiReq->maxTransfer, 5);
418: WRITE_REG(scsiCmd, SCMD_CLEAR_FIFO);
419: WRITE_REG(scsiDestID, scsiReq->target);
420: identify_msg = MSG_IDENTIFYMASK | (scsiReq->lun & MSG_ID_LUNMASK);
421: if(okToDisc) {
422: identify_msg |= MSG_ID_DISCONN;
423: }
424:
425: WRITE_REG(scsiFifo, identify_msg);
426:
427: /*
428: * Note this logic assumes that queue tag and SDTR messages are
429: * mutually exclusive...
430: */
431: if(SDTR_State == SNS_HOST_INIT_NEEDED) {
432: selectCmd = SCMD_SELECT_ATN_STOP;
433: }
434: else {
435: if(okToQueue) {
436: WRITE_REG(scsiFifo, MSG_SIMPLE_QUEUE_TAG);
437: WRITE_REG(scsiFifo, cmdBuf->queueTag);
438:
439: /*
440: * Save these in currMsgOut[] in case
441: * the target rejects this message.
442: */
443: currMsgOut[0] = MSG_SIMPLE_QUEUE_TAG;
444: currMsgOut[1] = cmdBuf->queueTag;
445: currMsgOutCnt = 2;
446: }
447: cp = (u_char *)cdbp;
448: for(i=0; i<cmdBuf->cdbLength; i++) {
449: WRITE_REG(scsiFifo, *cp++);
450: }
451: if(okToQueue) {
452: selectCmd = SCMD_SELECT_ATN_3;
453: }
454: else {
455: selectCmd = SCMD_SELECT_ATN;
456: }
457: }
458: WRITE_REG(scsiCmd, selectCmd);
459: IOGetTimestamp(&cmdBuf->startTime);
460: return HWS_OK;
461:
462: abortReq:
463: scsiReq->driverStatus = SR_IOST_CMDREJ;
464: [self ioComplete:cmdBuf];
465: return HWS_REJECT;
466: }
467:
468: /*
469: * SCSI device interrupt handler.
470: */
471: - (void)hwInterrupt
472: {
473: ddm_chip("hwInterrupt: activeCmd 0x%x\n", activeCmd, 2,3,4,5);
474:
475: switch([self scsiInterruptPending]) {
476: case SINT_NONE:
477: /*
478: * Must be another device....
479: */
480: [self enableAllInterrupts];
481: return;
482: case SINT_DEVICE:
483: case SINT_DMA:
484: break;
485: default:
486: /*
487: * What do we do now, batman?
488: */
489: [self hwAbort:SR_IOST_HW reason:"Bad Interrupt Received"];
490: return;
491: }
492:
493: goAgain:
494: /*
495: * Save interrupt state.
496: */
497: saveStatus = READ_REG(scsiStat);
498: saveSeqStep = READ_REG(internState);
499: saveIntrStatus = READ_REG(intrStatus);
500:
501: ddm_chip(" status 0x%x intstatus 0x%x scState %s\n",
502: saveStatus, saveIntrStatus,
503: IOFindNameForValue(scState, scStateValues), 4,5);
504: if((saveStatus & SS_ILLEGALOP) || (saveIntrStatus & IS_ILLEGALCMD)) {
505:
506: /*
507: * Software screwup. Start over from scratch.
508: */
509: IOLog("AMD53C974: hardware command reject\n");
510: [self hwAbort:SR_IOST_INT reason:"Hardware Command Reject"];
511: return;
512: }
513:
514: /*
515: * OK, grind thru the state machine.
516: */
517: switch(scState) {
518: case SCS_DISCONNECTED:
519: [self fsmDisconnected];
520: break;
521: case SCS_SELECTING:
522: [self fsmSelecting];
523: break;
524: case SCS_INITIATOR:
525: [self fsmInitiator];
526: break;
527: case SCS_COMPLETING:
528: [self fsmCompleting];
529: break;
530: case SCS_DMAING:
531: [self fsmDMAing];
532: break;
533: case SCS_ACCEPTINGMSG:
534: [self fsmAcceptingMsg];
535: break;
536: case SCS_SENDINGMSG:
537: [self fsmSendingMsg];
538: break;
539: case SCS_GETTINGMSG:
540: [self fsmGettingMsg];
541: break;
542: case SCS_SENDINGCMD:
543: [self fsmSendingCmd];
544: break;
545: default:
546: IOPanic("AMD53C974: Bad scState");
547: } /* switch scState */
548:
549: if ((scState != SCS_DISCONNECTED) &&
550: (saveIntrStatus & IS_DISCONNECT)) {
551: /*
552: * the target just up and went away. This is a catch-all
553: * trap for any unexpected disconnect.
554: */
555: ddm_err("hwInterrupt: target disconnected\n", 1,2,3,4,5);
556: scState = SCS_DISCONNECTED;
557: if(activeCmd != NULL) {
558: activeCmd->scsiReq->driverStatus = SR_IOST_TABT;
559: [self ioComplete:activeCmd];
560: activeCmd = NULL;
561: }
562: }
563:
564: /*
565: * Handle a SCSI Phase change if necessary.
566: */
567: if (scState == SCS_INITIATOR)
568: [self fsmPhaseChange];
569: #ifdef DEBUG
570: else {
571: ddm_chip("hwInterrupt #2: scState %s\n",
572: IOFindNameForValue(scState, scStateValues),
573: 2,3,4,5);
574: }
575: #endif DEBUG
576:
577: /*
578: * If we're off the bus, enable reselection at chip level.
579: */
580: if (scState == SCS_DISCONNECTED) {
581: ddm_chip("hwInterrupt: enabling reselection\n", 1,2,3,4,5);
582: WRITE_REG(scsiCmd, SCMD_ENABLE_SELECT);
583: }
584:
585:
586: /*
587: * If another SCSI interrupt is pending, go for it again (avoiding
588: * an unnecessary enableInterrupt and msg_receive()).
589: */
590: switch([self scsiInterruptPending]) {
591: case SINT_DEVICE:
592: #if INTR_LATENCY_TEST
593: ddm_chip("hwInterrupt: INTR TRUE; EXITING FOR MEASUREMENT\n",
594: 1,2,3,4,5);
595: break;
596: #else INTR_LATENCY_TEST
597: ddm_chip("hwInterrupt: going again without enabling "
598: "interrupt\n", 1,2,3,4,5);
599: goto goAgain;
600: #endif INTR_LATENCY_TEST
601: default:
602: break;
603: }
604:
605: [self enableAllInterrupts];
606:
607: /*
608: * One more thing - if we're still disconnected, enable processing
609: * of new commands.
610: */
611: if(scState == SCS_DISCONNECTED)
612: [self busFree];
613: ddm_chip("hwInterrupt: DONE; scState %s\n",
614: IOFindNameForValue(scState, scStateValues), 2,3,4,5);
615: }
616:
617:
618: - (void)logRegs
619: {
620: #if DEBUG
621: unsigned char cs, cis;
622: unsigned char fifoDepth;
623: unsigned scsiXfrCnt;
624: unsigned value;
625:
626: IOLog("*** saveStatus 0x%x saveIntrStatus 0x%x\n",
627: saveStatus, saveIntrStatus);
628: IOLog("*** scState = %s scsiCmd = 0x%x\n",
629: IOFindNameForValue(scState, scStateValues),
630: READ_REG(scsiCmd));
631:
632: cs = READ_REG(scsiStat);
633: cis = READ_REG(intrStatus);
634: IOLog("*** current status 0x%x current intrStatus 0x%x\n", cs, cis);
635:
636: IOLog("*** syncOffset %d syncPeriod 0x%x\n",
637: syncOffsetShadow, syncPeriodShadow);
638:
639: fifoDepth = READ_REG(currFifoState) & FS_FIFO_LEVEL_MASK;
640: scsiXfrCnt = READ_REG(currXfrCntLow);
641: value = READ_REG(currXfrCntMid);
642: scsiXfrCnt += (value << 8);
643: value = READ_REG(currXfrCntHi);
644: scsiXfrCnt += (value << 16);
645: IOLog("*** fifoDepth %d scsiXfrCnt 0x%x\n", fifoDepth, scsiXfrCnt);
646: #endif DEBUG
647: }
648:
649:
650: @end /* AMD_SCSI(Chip) */
651:
652: /* end of AMD_Chip.m */
653:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.