|
|
1.1 root 1: /* Copyright (c) 1994 NeXT Computer, Inc. All rights reserved.
2: *
3: * AMD_x86.m - architecture-specific methods for AMD SCSI driver
4: *
5: * HISTORY
6: * 21 Oct 94 Doug Mitchell at NeXT
7: * Created.
8: */
9:
10: #import <driverkit/generalFuncs.h>
11: #import <driverkit/kernelDriver.h>
12: #import <driverkit/i386/IOPCIDeviceDescription.h>
13: #import <kernserv/prototypes.h>
14: #import <mach/kern_return.h>
15: #import "AMD_x86.h"
16: #import "pciconf.h"
17: #import "AMD_Regs.h"
18: #import "AMD_Chip.h"
19: #import "bringup.h"
20: #import "AMD_ddm.h"
21: #import "configKeys.h"
22: #import <mach/mach_interface.h>
23:
24: #define TEST_DEBUG 0 /* low level I/O test before registerDevice */
25: #define TEST_IPL_BUG 0 /* test IPL bug */
26:
27: #if TEST_DEBUG
28: static void testDebug(id driver);
29: #endif TEST_DEBUG
30:
31: #if TEST_IPL_BUG
32: static void testIplBug();
33: #endif TEST_IPL_BUG
34:
35: #ifdef DEBUG
36: AMD_SCSI *amd_g;
37: #endif DEBUG
38:
39: static int _atoi(const char *ip)
40: {
41: unsigned rtn = 0;
42:
43: while(*ip) {
44: if((*ip < '0') || (*ip > '9')) {
45: return rtn;
46: }
47: rtn *= 10;
48: rtn += (*ip - '0');
49: ip++;
50: }
51: return rtn;
52: }
53:
54: /*
55: * Get I/O port range and IRQ from PCI config space. Set appropriate
56: * values in deviceDescription. Returns base address in *baseAddr.
57: * Returns YES if successful, else NO.
58: */
59: static BOOL parseConfigSpace(
60: id deviceDescription,
61: const char *title,
62: unsigned regSize, // in bytes
63: IOEISAPortAddress *baseAddr) // RETURNED
64: {
65: IOPCIConfigSpace configSpace;
66: IORange portRange;
67: unsigned *basePtr = 0;
68: int irq;
69: int i;
70: BOOL foundBase = NO;
71: IOReturn irtn;
72:
73: /*
74: * First get our configSpace register set.
75: */
76: bzero(&configSpace, sizeof(IOPCIConfigSpace));
77: if(irtn = [IODirectDevice getPCIConfigSpace:&configSpace
78: withDeviceDescription:deviceDescription]) {
79: IOLog("%s: Can\'t get configSpace (%s); ABORTING\n",
80: title, [IODirectDevice stringFromReturn:irtn]);
81: return NO;
82: }
83: basePtr = configSpace.BaseAddress;
84: irq = configSpace.InterruptLine;
85: if((basePtr[0] == 0) || (irq == 0)) {
86: IOLog("%s: Bogus config info (IRQ %d, Base 0x%x)\n",
87: title, irq, (unsigned)basePtr);
88: return NO;
89: }
90:
91: /*
92: * Scan all 6 base address registers, make sure there is exactly one
93: * I/O address.
94: */
95: for(i=0; i<PCI_NUM_BASE_ADDRESS; i++) {
96: if(basePtr[i] & PCI_BASE_IO_BIT) {
97: if(foundBase) {
98: IOLog("%s: Multiple I/O Port Bases Found\n", title);
99: return NO;
100: }
101: foundBase = YES;
102: portRange.start = PCI_BASE_IO(basePtr[i]);
103: }
104: }
105: if(!foundBase) {
106: IOLog("%s: No I/O Port Base Found\n", title);
107: return NO;
108: }
109: portRange.size = regSize;
110: *baseAddr = portRange.start;
111: ddm_init("irq %d base 0x%x\n", irq, *baseAddr, 3,4,5);
112:
113: /*
114: * OK, retweeze our device description.
115: */
116: irtn = [deviceDescription setInterruptList:&irq num:1];
117: if(irtn) {
118: IOLog("%s: Can\'t set interruptList to IRQ %d (%s)\n",
119: title, irq, [IODirectDevice stringFromReturn:irtn]);
120: return NO;
121: }
122: irtn = [deviceDescription setPortRangeList:&portRange num:1];
123: if(irtn) {
124: IOLog("%s: Can\'t set portRangeList to port 0x%x (%s)\n",
125: title, portRange.start,
126: [IODirectDevice stringFromReturn:irtn]);
127: return NO;
128: }
129: return YES;
130: }
131:
132: /*
133: * Obtain a YES/NO type parameter from the config table.
134: */
135: static int getConfigParam(
136: id configTable,
137: const char *paramName)
138: {
139: const char *value;
140: int rtn = 0; // default if not present in table
141:
142: value = [configTable valueForStringKey:paramName];
143: if(value) {
144: if(strcmp(value, "YES") == 0) {
145: rtn = 1;
146: }
147: [configTable freeString:value];
148: }
149: return rtn;
150: }
151:
152: @implementation AMD_SCSI(Architecture)
153:
154: - archInit : deviceDescription
155: {
156: id configTable;
157: const char *value = NULL;
158: kern_return_t krtn;
159: vm_offset_t startPage, endPage;
160: unsigned ival;
161: IOReturn irtn;
162: unsigned char lun;
163:
164: #if TEST_IPL_BUG
165: testIplBug();
166: #endif TEST_IPL_BUG
167:
168: ddm_init("AMD archInit\n",
169: 1,2,3,4,5);
170: scState = SCS_UNINITIALIZED;
171:
172: /*
173: * Obtain I/O port base, busType dependent.
174: */
175: levelIRQ = NO;
176: configTable = [deviceDescription configTable];
177: value = [configTable valueForStringKey:"Bus Type"];
178: if(value == NULL) {
179: IOLog("AMD53C974: No Bus Type in config Table\n");
180: goto abort;
181: }
182: if(strcmp(value, "PCI") == 0) {
183: busType = BT_PCI;
184: if(parseConfigSpace(deviceDescription,
185: "AMD53C974",
186: AMD_PCI_REGISTER_SPACE,
187: &ioBase) == NO) {
188: [configTable freeString:value];
189: goto abort;
190: }
191: ioBase += AMD_PCI_REGISTER_OFFSET;
192: if(irtn = [deviceDescription getPCIdevice : &deviceNumber
193: function : &functionNumber
194: bus : &busNumber]) {
195: IOLog("AMD53C974: Can't find device using "
196: "getPCIdevice (%s)\n",
197: [self stringFromReturn:irtn]);
198: goto abort;
199: }
200: levelIRQ = YES;
201: IOLog("AMD53C974: found at bus %d device %d function %d "
202: "irq %d\n",
203: busNumber, deviceNumber, functionNumber,
204: [deviceDescription interrupt]);
205: }
206: else {
207: IOLog("AMD53C974: Bad Bus Type (%s) in config table\n",
208: value);
209: [configTable freeString:value];
210: goto abort;
211: }
212: [configTable freeString:value];
213:
214: if (![self probeChip]) {
215: IOLog("AMD53C974 Host Adaptor Not found at Port 0x%x\n",
216: ioBase);
217: goto abort;
218: }
219:
220: #if DEBUG
221: amd_g = self;
222: #endif DEBUG
223:
224: if ([super initFromDeviceDescription:deviceDescription] == nil) {
225: goto abort;
226: }
227: ioThreadRunning = 1;
228:
229: /*
230: * Initialize local variables. Note that activeArray and
231: * perTarget arrays are zeroed by objc runtime.
232: */
233: queue_init(&disconnectQ);
234: queue_init(&commandQ);
235: queue_init(&pendingQ);
236: commandLock = [[NXLock alloc] init];
237: activeCmd = NULL;
238: [self resetStats];
239: nextQueueTag = QUEUE_TAG_NONTAGGED + 1;
240:
241: /*
242: * Allocate some physically contiguous memory for the Memory
243: * Descriptor List.
244: */
245: mdlFree = IOMalloc(MDL_SIZE * 2 * sizeof(vm_address_t));
246: startPage = trunc_page(mdlFree);
247: endPage = trunc_page(((vm_offset_t)&mdlFree[MDL_SIZE]) - 1);
248: if(startPage != endPage) {
249: mdl = mdlFree + MDL_SIZE;
250: }
251: else {
252: mdl = mdlFree;
253: }
254: ddm_init("&mdl[0] = 0x%x &mdl[%d] = 0x%x\n", mdl, MDL_SIZE - 1,
255: &mdl[MDL_SIZE - 1], 4,5);
256: irtn = IOPhysicalFromVirtual(IOVmTaskSelf(),
257: (vm_offset_t)mdl,
258: &mdlPhys);
259: if(irtn) {
260: IOLog("AMD53C974: can't get physical address of MDL\n");
261: goto abort;
262: }
263:
264: /*
265: * get tagged command queueing, sync mode, fast mode enables from
266: * configTable.
267: */
268: cmdQueueEnable = getConfigParam(configTable, CMD_QUEUE_ENABLE);
269: syncModeEnable = getConfigParam(configTable, SYNC_ENABLE);
270: fastModeEnable = getConfigParam(configTable, FAST_ENABLE);
271: extendTiming = getConfigParam(configTable, EXTENDED_TIMING);
272:
273: /*
274: * Get clock rate, in MHz.
275: */
276: scsiClockRate = AMD_DEFAULT_CLOCK;
277: value = [configTable valueForStringKey:SCSI_CLOCK_RATE];
278: if(value) {
279: ival = _atoi(value);
280: if(ival) {
281: scsiClockRate = ival;
282: ddm_init("SCSI Clock Rate = %d MHz\n",
283: ival, 2,3,4,5);
284: }
285: [configTable freeString:value];
286: }
287:
288: autoSenseEnable = AUTO_SENSE_ENABLE; // from bringup.h
289:
290: /*
291: * Get internal version of interruptPort; set the port queue
292: * length to the maximum size.
293: */
294: interruptPortKern = IOConvertPort([self interruptPort],
295: IO_KernelIOTask,
296: IO_Kernel);
297: krtn = port_set_backlog(task_self(), [self interruptPort],
298: PORT_BACKLOG_MAX);
299: if(krtn) {
300: IOLog("%s: error %d on port_set_backlog()\n",
301: [self name], krtn);
302: /* Oh well... */
303: }
304:
305: /*
306: * Initialize the chip and reset the bus.
307: */
308: if([self hwReset:NULL]) {
309: goto abort;
310: }
311:
312: /*
313: * Reserve our devices. hostId is init'd at chip level in hwReset.
314: */
315: for(lun=0; lun<SCSI_NLUNS; lun++) {
316: [self reserveTarget:hostId
317: lun:lun
318: forOwner:self];
319: }
320:
321: /*
322: * OK, we're ready to roll.
323: */
324:
325: #if TEST_DEBUG
326:
327: /*
328: * Before we call registerDevice and bring all kinds of uncontrolled
329: * I/O...
330: */
331: testDebug(self);
332: #endif TEST_DEBUG
333:
334: [self registerDevice];
335:
336: return self;
337:
338: abort:
339: return [self free];
340: }
341:
342: /*
343: * Ensure DMA machine is in idle quiescent state.
344: */
345: - (void)dmaIdle
346: {
347: unsigned cmd = DC_CMD_IDLE | DC_MDL;
348:
349: /*
350: * MDL and dir bits need to be the same as they will for a
351: * (potentially) upcoming DMA command.
352: */
353: if(activeCmd) {
354: if(activeCmd->scsiReq->read) {
355: cmd |= DC_DIR_READ;
356: }
357: else {
358: cmd |= DC_DIR_WRITE;
359: }
360: }
361: /* else direction is don't care */
362:
363: /*
364: * FIXME - should we do a DMA blast here?
365: */
366: WRITE_REGL(dmaCommand, cmd);
367: #if WRITE_DMA_COMMAND_TWICE
368: WRITE_REGL(dmaCommand, cmd);
369: #endif WRITE_DMA_COMMAND_TWICE
370: }
371:
372: #if DDM_DEBUG
373: static unsigned char *ddmPhys;
374: #endif DDM_DEBUG
375:
376: /*
377: * Start DMA transfer at activeCmd->currentPtr for activeCmd->currentByteCount.
378: * Note: this method is not strictly architecture-dependent and
379: * chip-independent. I think it's best to do all of this work in one place,
380: * and an AMD chip for a different bus will definitely have a lot of changes
381: * here.
382: */
383: - (sc_status_t)dmaStart
384: {
385: unsigned byteCount = activeCmd->currentByteCount;
386: unsigned char cvalue;
387: unsigned pages;
388: vm_offset_t virtAddr;
389: unsigned physAddr;
390: unsigned page;
391: unsigned offset;
392: IOReturn irtn;
393: unsigned cmd;
394:
395: ddm_thr("dmaStart\n", 1,2,3,4,5);
396: ASSERT(activeCmd != NULL);
397: [self dmaIdle];
398:
399: /*
400: * Set up SCSI block transfer count registers.
401: */
402: cvalue = byteCount & 0xff;
403: WRITE_REG(startXfrCntLow, cvalue);
404: cvalue = (byteCount >> 8) & 0xff;
405: WRITE_REG(startXfrCntMid, cvalue);
406: cvalue = (byteCount >> 16) & 0xff;
407: WRITE_REG(startXfrCntHi, cvalue);
408:
409: /*
410: * Set up a memory descriptor list.
411: */
412: virtAddr = (vm_offset_t)activeCmd->currentPtr;
413: pages = (AMD_ROUND_PAGE(virtAddr + byteCount) -
414: AMD_TRUNC_PAGE(virtAddr)) / AMD_DMA_PAGE_SIZE;
415: for(page=0; page<pages; page++) {
416: if(page == 0) {
417: /*
418: * Special case, this one is allowed a page offset.
419: */
420: offset = virtAddr & AMD_DMA_PAGE_MASK;
421: WRITE_REGL(dmaStartAddrs, offset);
422: ddm_dma(" page 0 offset 0x%x\n", offset, 2,3,4,5);
423: virtAddr = virtAddr & ~AMD_DMA_PAGE_MASK;
424: }
425: irtn = IOPhysicalFromVirtual(activeCmd->client,
426: virtAddr,
427: &physAddr);
428: if(irtn) {
429: IOLog("%s: Can't get physical address (%s)\n",
430: [self name], [self stringFromReturn:irtn]);
431: return SR_IOST_MEMF;
432: }
433: ddm_dma(" mdl[%d] = 0x%x\n", page, mdl[page], 3,4,5);
434: mdl[page] = physAddr;
435: virtAddr += AMD_DMA_PAGE_SIZE;
436: #if DDM_DEBUG
437: if(page == 0) {
438: ddmPhys = (unsigned char *)(physAddr + offset);
439: }
440: #endif DDM_DEBUG
441: }
442:
443: /*
444: * Load byte count and address of MDL into DMA engine, and go.
445: */
446: ddm_dma(" dmaStartCount = 0x%x\n", byteCount, 2,3,4,5);
447: WRITE_REGL(dmaStartCount, byteCount);
448: WRITE_REGL(dmaStartMdlAddrs, mdlPhys);
449: if(activeCmd->scsiReq->read) {
450: cmd = DC_CMD_START | DC_MDL | DC_DIR_READ;
451: }
452: else {
453: cmd = DC_CMD_START | DC_MDL | DC_DIR_WRITE;
454: }
455: WRITE_REGL(dmaCommand, cmd);
456: #if WRITE_DMA_COMMAND_TWICE
457: WRITE_REGL(dmaCommand, cmd);
458: #endif WRITE_DMA_COMMAND_TWICE
459: WRITE_REG(scsiCmd, SCMD_TRANSFER_INFO | SCMD_ENABLEDMA);
460: scState = SCS_DMAING;
461: return SR_IOST_GOOD;
462: }
463:
464: /*
465: * Terminate a DMA, including FIFO flush if necessary. Returns number of
466: * bytes transferred.
467: */
468: - (unsigned)dmaTerminate
469: {
470: unsigned char fifoDepth = 0;
471: unsigned cmd;
472: int tries;
473: unsigned status;
474: unsigned bytesXfrd;
475: unsigned scsiXfrCnt;
476: unsigned value;
477:
478: ASSERT(activeCmd != NULL);
479:
480: /*
481: * Get resid count from SCSI block.
482: */
483: scsiXfrCnt = READ_REG(currXfrCntLow);
484: value = READ_REG(currXfrCntMid);
485: scsiXfrCnt += (value << 8);
486: value = READ_REG(currXfrCntHi);
487: scsiXfrCnt += (value << 16);
488:
489: fifoDepth = READ_REG(currFifoState) & FS_FIFO_LEVEL_MASK;
490: if((activeCmd->scsiReq->read) && (scsiXfrCnt != 0)) {
491: /*
492: * Make sure SCSI fifo is empty. The manual says we
493: * might have to wait a while.
494: */
495: if(fifoDepth) {
496: IODelay(1000);
497: fifoDepth = READ_REG(currFifoState) &
498: FS_FIFO_LEVEL_MASK;
499: switch(fifoDepth) {
500: case 0:
501: ddm_dma("dmaTerminate: fifo cleared\n",
502: 1,2,3,4,5);
503: break; // normal, OK
504: case 1:
505: IOLog("%s: Odd Byte Disconnect on target %d\n",
506: [self name],
507: activeCmd->scsiReq->target);
508: break;
509: default:
510: IOLog("%s: SCSI FIFO hung\n", [self name]);
511: break;
512:
513: /*
514: * I'm not sure what to do about these
515: * errors...
516: */
517: }
518: }
519: if(activeCmd->scsiReq->read) {
520: cmd = DC_CMD_BLAST | DC_MDL | DC_DIR_READ;
521: }
522: else {
523: cmd = DC_CMD_BLAST | DC_MDL | DC_DIR_WRITE;
524: }
525: ddm_dma(" ...sending DMA blast\n", 1,2,3,4,5);
526: WRITE_REGL(dmaCommand, cmd);
527: #if WRITE_DMA_COMMAND_TWICE
528: WRITE_REGL(dmaCommand, cmd);
529: #endif WRITE_DMA_COMMAND_TWICE
530:
531: /*
532: * Unfortunately, we have to poll for this one. No interrupt.
533: * FIXME - documentation is unclear on this. 6.7.6, the
534: * description of dmaStatus, says DS_BLAST_COMPLETE is only
535: * complete for "SCSI Disconnect and Reselect Operation".
536: * That doesn't make a whole lot of sense to me...
537: */
538: for(tries=0; tries<500; tries++) {
539: status = READ_REGL(dmaStatus);
540: if(status & DS_BLAST_COMPLETE) {
541: break;
542: }
543: IODelay(100);
544: }
545:
546: ddm_dma("DMA blast : tries = %d fifoDepth = %d\n",
547: tries, fifoDepth, 3,4,5);
548: }
549:
550: /*
551: * Obtain number of bytes transferred.
552: */
553: bytesXfrd = activeCmd->currentByteCount -
554: (scsiXfrCnt + fifoDepth);
555: ddm_chip("dmaTerminate: currentByteCount 0x%x, bytesXfrd 0x%x\n",
556: activeCmd->currentByteCount, bytesXfrd, 3,4,5);
557: #if 0
558: {
559: unsigned char *vp = activeCmd->buffer;
560:
561: ddm_init("ddmPhys = %02x %02x %02x %02x %02x\n",
562: ddmPhys[0], ddmPhys[1], ddmPhys[2], ddmPhys[3], ddmPhys[4]);
563: ddm_init(" %02x %02x %02x %02x %02x\n",
564: ddmPhys[5], ddmPhys[6], ddmPhys[7], ddmPhys[8], ddmPhys[9]);
565: ddm_init("virt = %02x %02x %02x %02x %02x\n",
566: vp[0], vp[1], vp[2], vp[3], vp[4]);
567: ddm_init(" %02x %02x %02x %02x %02x\n",
568: vp[5], vp[6], vp[7], vp[8], vp[9]);
569: }
570: #endif 0
571: [self dmaIdle];
572: return bytesXfrd;
573: }
574:
575: @end
576:
577: #if TEST_DEBUG
578:
579: /*
580: * Do some simple I/O before IODisk starts probing us.
581: */
582: int loopTest = 0;
583: int target = 0;
584:
585: #define DO_INIT_SLEEP 0
586: #define DO_TUR 1
587: #define DO_READ 1
588: #define TEST_READ_SIZE 1 // in sectors
589: #define DO_TEST_ALIGN 0
590: #define TEST_DISCONNECT 1
591:
592: static void testDebug(id driver)
593: {
594: IOSCSIRequest scsiReq;
595: sc_status_t srtn;
596: unsigned char *rbuf;
597: int block = 100000;
598:
599: ddm_init("testDebug\n", 1,2,3,4,5);
600: if(DO_INIT_SLEEP) {
601: IOLog("Sleeping for 10 seconds for DDM view\n");
602: IOSleep(10000);
603: }
604: if(DO_READ) {
605: if(DO_TEST_ALIGN) {
606: rbuf = IOMallocLow(TEST_READ_SIZE * 512);
607: if(rbuf == NULL) {
608: IOLog("IOMallocLow returned NULL!\n");
609: rbuf = IOMalloc(TEST_READ_SIZE * 512);
610: }
611: }
612: else {
613: rbuf = IOMalloc(TEST_READ_SIZE * 512);
614: }
615: }
616: do {
617: if(DO_TUR) {
618: bzero(&scsiReq, sizeof(IOSCSIRequest));
619: scsiReq.target = target;
620: scsiReq.cmdQueueDisable = 1;
621:
622: /*
623: * cdb = all zeroes = test unit ready
624: */
625: scsiReq.timeoutLength = 4;
626: srtn = [driver executeRequest:&scsiReq
627: buffer:NULL
628: client:(vm_task_t)0];
629: IOLog("testDebug: TUR result = %s\n",
630: IOFindNameForValue(scsiReq.driverStatus,
631: IOScStatusStrings));
632: }
633:
634: if(DO_READ) {
635: cdb_6_t *cdbp = &scsiReq.cdb.cdb_c6;
636: unsigned i;
637:
638: bzero(&scsiReq, sizeof(IOSCSIRequest));
639: scsiReq.target = target;
640: scsiReq.cmdQueueDisable = 1;
641: scsiReq.disconnect = TEST_DISCONNECT;
642: for(i=0; i<(TEST_READ_SIZE * 512); i++) {
643: rbuf[i] = i;
644: }
645: cdbp->c6_opcode = 8;
646: cdbp->c6_len = TEST_READ_SIZE;
647: cdbp->c6_lba0 = block;
648: /*
649: * Force a disconnect eventually
650: */
651: if(block == 0) {
652: block = 100000;
653: }
654: else {
655: block = 0;
656: }
657: scsiReq.maxTransfer = TEST_READ_SIZE * 512;
658: scsiReq.read = YES;
659: scsiReq.timeoutLength = 10;
660: srtn = [driver executeRequest:&scsiReq
661: buffer:rbuf
662: client:IOVmTaskSelf()];
663: if(scsiReq.driverStatus == 0) {
664: ddm_init("rbuf = %02x %02x %02x %02x %02x\n",
665: rbuf[0],rbuf[1],rbuf[2],rbuf[3],rbuf[4]);
666: ddm_init(" %02x %02x %02x %02x %02x\n",
667: rbuf[5],rbuf[6],rbuf[7],rbuf[8],rbuf[9]);
668: }
669: IOLog("testDebug: Read result = %s\n",
670: IOFindNameForValue(scsiReq.driverStatus,
671: IOScStatusStrings));
672: }
673: IOSleep(5000);
674: } while(loopTest);
675:
676: }
677:
678: #endif TEST_DEBUG
679:
680: #if TEST_IPL_BUG
681:
682: #define IPL_TEST_LOOPS 1000000 // # of loops
683: #define IPL_TEST_TIME 0 // us delay per loop
684:
685: static void testIplBug() {
686: int loopNum;
687: ns_time_t curTime, lastTime;
688: unsigned usTime;
689:
690: IOGetTimestamp(&lastTime);
691: for(loopNum=0; loopNum<IPL_TEST_LOOPS; loopNum++) {
692: if(IPL_TEST_TIME) {
693: IODelay(IPL_TEST_TIME);
694: }
695: IOGetTimestamp(&curTime);
696: usTime = (unsigned)((curTime - lastTime) / 1000ULL);
697: if(usTime > 1000) {
698: ddm_intr("usTime %d loopNum %d curTime 0x%x lastTime "
699: "0x%x\n",
700: usTime, loopNum,
701: (unsigned)(curTime & 0xffffffffULL),
702: (unsigned)(lastTime & 0xffffffffULL), 5);
703: }
704: lastTime = curTime;
705:
706: }
707: ddm_intr("testIplBug complete; IPL_TEST_TIME %d\n", IPL_TEST_TIME,
708: 2,3,4,5);
709:
710: /*
711: * Calibrate the IODelay call...
712: *
713: for(loopNum=0; loopNum<500; loopNum++) {
714: IODelay(IPL_TEST_TIME);
715: ddm_intr("IODelay(%d) calibration\n", IPL_TEST_TIME, 2,3,4,5);
716: }
717: */
718: }
719:
720: #endif TEST_IPL_BUG
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.