home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (c) 1994 NeXT Computer, Inc. All rights reserved.
- *
- * AMD_x86.m - architecture-specific methods for AMD SCSI driver
- *
- * HISTORY
- * 21 Oct 94 Doug Mitchell at NeXT
- * Created.
- */
-
- #import <driverkit/generalFuncs.h>
- #import <driverkit/kernelDriver.h>
- #import <driverkit/i386/IOPCIDeviceDescription.h>
- #import <kernserv/prototypes.h>
- #import <mach/kern_return.h>
- #import "AMD_x86.h"
- #import "pciconf.h"
- #import "AMD_Regs.h"
- #import "AMD_Chip.h"
- #import "bringup.h"
- #import "AMD_ddm.h"
- #import "configKeys.h"
- #import <mach/mach_interface.h>
-
- #define TEST_DEBUG 0 /* low level I/O test before registerDevice */
- #define TEST_IPL_BUG 0 /* test IPL bug */
-
- #if TEST_DEBUG
- static void testDebug(id driver);
- #endif TEST_DEBUG
-
- #if TEST_IPL_BUG
- static void testIplBug();
- #endif TEST_IPL_BUG
-
- #ifdef DEBUG
- AMD_SCSI *amd_g;
- #endif DEBUG
-
- static int _atoi(const char *ip)
- {
- unsigned rtn = 0;
-
- while(*ip) {
- if((*ip < '0') || (*ip > '9')) {
- return rtn;
- }
- rtn *= 10;
- rtn += (*ip - '0');
- ip++;
- }
- return rtn;
- }
-
- /*
- * Get I/O port range and IRQ from PCI config space. Set appropriate
- * values in deviceDescription. Returns base address in *baseAddr.
- * Returns YES if successful, else NO.
- */
- static BOOL parseConfigSpace(
- id deviceDescription,
- const char *title,
- unsigned regSize, // in bytes
- IOEISAPortAddress *baseAddr) // RETURNED
- {
- IOPCIConfigSpace configSpace;
- IORange portRange;
- unsigned *basePtr = 0;
- int irq;
- int i;
- BOOL foundBase = NO;
- IOReturn irtn;
-
- /*
- * First get our configSpace register set.
- */
- bzero(&configSpace, sizeof(IOPCIConfigSpace));
- if(irtn = [IODirectDevice getPCIConfigSpace:&configSpace
- withDeviceDescription:deviceDescription]) {
- IOLog("%s: Can\'t get configSpace (%s); ABORTING\n",
- title, [IODirectDevice stringFromReturn:irtn]);
- return NO;
- }
- basePtr = configSpace.BaseAddress;
- irq = configSpace.InterruptLine;
- if((basePtr[0] == 0) || (irq == 0)) {
- IOLog("%s: Bogus config info (IRQ %d, Base 0x%x)\n",
- title, irq, (unsigned)basePtr);
- return NO;
- }
-
- /*
- * Scan all 6 base address registers, make sure there is exactly one
- * I/O address.
- */
- for(i=0; i<PCI_NUM_BASE_ADDRESS; i++) {
- if(basePtr[i] & PCI_BASE_IO_BIT) {
- if(foundBase) {
- IOLog("%s: Multiple I/O Port Bases Found\n", title);
- return NO;
- }
- foundBase = YES;
- portRange.start = PCI_BASE_IO(basePtr[i]);
- }
- }
- if(!foundBase) {
- IOLog("%s: No I/O Port Base Found\n", title);
- return NO;
- }
- portRange.size = regSize;
- *baseAddr = portRange.start;
- ddm_init("irq %d base 0x%x\n", irq, *baseAddr, 3,4,5);
-
- /*
- * OK, retweeze our device description.
- */
- irtn = [deviceDescription setInterruptList:&irq num:1];
- if(irtn) {
- IOLog("%s: Can\'t set interruptList to IRQ %d (%s)\n",
- title, irq, [IODirectDevice stringFromReturn:irtn]);
- return NO;
- }
- irtn = [deviceDescription setPortRangeList:&portRange num:1];
- if(irtn) {
- IOLog("%s: Can\'t set portRangeList to port 0x%x (%s)\n",
- title, portRange.start,
- [IODirectDevice stringFromReturn:irtn]);
- return NO;
- }
- return YES;
- }
-
- /*
- * Obtain a YES/NO type parameter from the config table.
- */
- static int getConfigParam(
- id configTable,
- const char *paramName)
- {
- const char *value;
- int rtn = 0; // default if not present in table
-
- value = [configTable valueForStringKey:paramName];
- if(value) {
- if(strcmp(value, "YES") == 0) {
- rtn = 1;
- }
- [configTable freeString:value];
- }
- return rtn;
- }
-
- @implementation AMD_SCSI(Architecture)
-
- - archInit : deviceDescription
- {
- id configTable;
- const char *value = NULL;
- kern_return_t krtn;
- vm_offset_t startPage, endPage;
- unsigned ival;
- IOReturn irtn;
- unsigned char lun;
-
- #if TEST_IPL_BUG
- testIplBug();
- #endif TEST_IPL_BUG
-
- ddm_init("AMD archInit\n",
- 1,2,3,4,5);
- scState = SCS_UNINITIALIZED;
-
- /*
- * Obtain I/O port base, busType dependent.
- */
- levelIRQ = NO;
- configTable = [deviceDescription configTable];
- value = [configTable valueForStringKey:"Bus Type"];
- if(value == NULL) {
- IOLog("AMD53C974: No Bus Type in config Table\n");
- goto abort;
- }
- if(strcmp(value, "PCI") == 0) {
- busType = BT_PCI;
- if(parseConfigSpace(deviceDescription,
- "AMD53C974",
- AMD_PCI_REGISTER_SPACE,
- &ioBase) == NO) {
- [configTable freeString:value];
- goto abort;
- }
- ioBase += AMD_PCI_REGISTER_OFFSET;
- if(irtn = [deviceDescription getPCIdevice : &deviceNumber
- function : &functionNumber
- bus : &busNumber]) {
- IOLog("AMD53C974: Can't find device using "
- "getPCIdevice (%s)\n",
- [self stringFromReturn:irtn]);
- goto abort;
- }
- levelIRQ = YES;
- IOLog("AMD53C974: found at bus %d device %d function %d "
- "irq %d\n",
- busNumber, deviceNumber, functionNumber,
- [deviceDescription interrupt]);
- }
- else {
- IOLog("AMD53C974: Bad Bus Type (%s) in config table\n",
- value);
- [configTable freeString:value];
- goto abort;
- }
- [configTable freeString:value];
-
- if (![self probeChip]) {
- IOLog("AMD53C974 Host Adaptor Not found at Port 0x%x\n",
- ioBase);
- goto abort;
- }
-
- #if DEBUG
- amd_g = self;
- #endif DEBUG
-
- if ([super initFromDeviceDescription:deviceDescription] == nil) {
- goto abort;
- }
- ioThreadRunning = 1;
-
- /*
- * Initialize local variables. Note that activeArray and
- * perTarget arrays are zeroed by objc runtime.
- */
- queue_init(&disconnectQ);
- queue_init(&commandQ);
- queue_init(&pendingQ);
- commandLock = [[NXLock alloc] init];
- activeCmd = NULL;
- [self resetStats];
- nextQueueTag = QUEUE_TAG_NONTAGGED + 1;
-
- /*
- * Allocate some physically contiguous memory for the Memory
- * Descriptor List.
- */
- mdlFree = IOMalloc(MDL_SIZE * 2 * sizeof(vm_address_t));
- startPage = trunc_page(mdlFree);
- endPage = trunc_page(((vm_offset_t)&mdlFree[MDL_SIZE]) - 1);
- if(startPage != endPage) {
- mdl = mdlFree + MDL_SIZE;
- }
- else {
- mdl = mdlFree;
- }
- ddm_init("&mdl[0] = 0x%x &mdl[%d] = 0x%x\n", mdl, MDL_SIZE - 1,
- &mdl[MDL_SIZE - 1], 4,5);
- irtn = IOPhysicalFromVirtual(IOVmTaskSelf(),
- (vm_offset_t)mdl,
- &mdlPhys);
- if(irtn) {
- IOLog("AMD53C974: can't get physical address of MDL\n");
- goto abort;
- }
-
- /*
- * get tagged command queueing, sync mode, fast mode enables from
- * configTable.
- */
- cmdQueueEnable = getConfigParam(configTable, CMD_QUEUE_ENABLE);
- syncModeEnable = getConfigParam(configTable, SYNC_ENABLE);
- fastModeEnable = getConfigParam(configTable, FAST_ENABLE);
- extendTiming = getConfigParam(configTable, EXTENDED_TIMING);
-
- /*
- * Get clock rate, in MHz.
- */
- scsiClockRate = AMD_DEFAULT_CLOCK;
- value = [configTable valueForStringKey:SCSI_CLOCK_RATE];
- if(value) {
- ival = _atoi(value);
- if(ival) {
- scsiClockRate = ival;
- ddm_init("SCSI Clock Rate = %d MHz\n",
- ival, 2,3,4,5);
- }
- [configTable freeString:value];
- }
-
- autoSenseEnable = AUTO_SENSE_ENABLE; // from bringup.h
-
- /*
- * Get internal version of interruptPort; set the port queue
- * length to the maximum size.
- */
- interruptPortKern = IOConvertPort([self interruptPort],
- IO_KernelIOTask,
- IO_Kernel);
- krtn = port_set_backlog(task_self(), [self interruptPort],
- PORT_BACKLOG_MAX);
- if(krtn) {
- IOLog("%s: error %d on port_set_backlog()\n",
- [self name], krtn);
- /* Oh well... */
- }
-
- /*
- * Initialize the chip and reset the bus.
- */
- if([self hwReset:NULL]) {
- goto abort;
- }
-
- /*
- * Reserve our devices. hostId is init'd at chip level in hwReset.
- */
- for(lun=0; lun<SCSI_NLUNS; lun++) {
- [self reserveTarget:hostId
- lun:lun
- forOwner:self];
- }
-
- /*
- * OK, we're ready to roll.
- */
-
- #if TEST_DEBUG
-
- /*
- * Before we call registerDevice and bring all kinds of uncontrolled
- * I/O...
- */
- testDebug(self);
- #endif TEST_DEBUG
-
- [self registerDevice];
-
- return self;
-
- abort:
- return [self free];
- }
-
- /*
- * Ensure DMA machine is in idle quiescent state.
- */
- - (void)dmaIdle
- {
- unsigned cmd = DC_CMD_IDLE | DC_MDL;
-
- /*
- * MDL and dir bits need to be the same as they will for a
- * (potentially) upcoming DMA command.
- */
- if(activeCmd) {
- if(activeCmd->scsiReq->read) {
- cmd |= DC_DIR_READ;
- }
- else {
- cmd |= DC_DIR_WRITE;
- }
- }
- /* else direction is don't care */
-
- /*
- * FIXME - should we do a DMA blast here?
- */
- WRITE_REGL(dmaCommand, cmd);
- #if WRITE_DMA_COMMAND_TWICE
- WRITE_REGL(dmaCommand, cmd);
- #endif WRITE_DMA_COMMAND_TWICE
- }
-
- #if DDM_DEBUG
- static unsigned char *ddmPhys;
- #endif DDM_DEBUG
-
- /*
- * Start DMA transfer at activeCmd->currentPtr for activeCmd->currentByteCount.
- * Note: this method is not strictly architecture-dependent and
- * chip-independent. I think it's best to do all of this work in one place,
- * and an AMD chip for a different bus will definitely have a lot of changes
- * here.
- */
- - (sc_status_t)dmaStart
- {
- unsigned byteCount = activeCmd->currentByteCount;
- unsigned char cvalue;
- unsigned pages;
- vm_offset_t virtAddr;
- unsigned physAddr;
- unsigned page;
- unsigned offset;
- IOReturn irtn;
- unsigned cmd;
-
- ddm_thr("dmaStart\n", 1,2,3,4,5);
- ASSERT(activeCmd != NULL);
- [self dmaIdle];
-
- /*
- * Set up SCSI block transfer count registers.
- */
- cvalue = byteCount & 0xff;
- WRITE_REG(startXfrCntLow, cvalue);
- cvalue = (byteCount >> 8) & 0xff;
- WRITE_REG(startXfrCntMid, cvalue);
- cvalue = (byteCount >> 16) & 0xff;
- WRITE_REG(startXfrCntHi, cvalue);
-
- /*
- * Set up a memory descriptor list.
- */
- virtAddr = (vm_offset_t)activeCmd->currentPtr;
- pages = (AMD_ROUND_PAGE(virtAddr + byteCount) -
- AMD_TRUNC_PAGE(virtAddr)) / AMD_DMA_PAGE_SIZE;
- for(page=0; page<pages; page++) {
- if(page == 0) {
- /*
- * Special case, this one is allowed a page offset.
- */
- offset = virtAddr & AMD_DMA_PAGE_MASK;
- WRITE_REGL(dmaStartAddrs, offset);
- ddm_dma(" page 0 offset 0x%x\n", offset, 2,3,4,5);
- virtAddr = virtAddr & ~AMD_DMA_PAGE_MASK;
- }
- irtn = IOPhysicalFromVirtual(activeCmd->client,
- virtAddr,
- &physAddr);
- if(irtn) {
- IOLog("%s: Can't get physical address (%s)\n",
- [self name], [self stringFromReturn:irtn]);
- return SR_IOST_MEMF;
- }
- ddm_dma(" mdl[%d] = 0x%x\n", page, mdl[page], 3,4,5);
- mdl[page] = physAddr;
- virtAddr += AMD_DMA_PAGE_SIZE;
- #if DDM_DEBUG
- if(page == 0) {
- ddmPhys = (unsigned char *)(physAddr + offset);
- }
- #endif DDM_DEBUG
- }
-
- /*
- * Load byte count and address of MDL into DMA engine, and go.
- */
- ddm_dma(" dmaStartCount = 0x%x\n", byteCount, 2,3,4,5);
- WRITE_REGL(dmaStartCount, byteCount);
- WRITE_REGL(dmaStartMdlAddrs, mdlPhys);
- if(activeCmd->scsiReq->read) {
- cmd = DC_CMD_START | DC_MDL | DC_DIR_READ;
- }
- else {
- cmd = DC_CMD_START | DC_MDL | DC_DIR_WRITE;
- }
- WRITE_REGL(dmaCommand, cmd);
- #if WRITE_DMA_COMMAND_TWICE
- WRITE_REGL(dmaCommand, cmd);
- #endif WRITE_DMA_COMMAND_TWICE
- WRITE_REG(scsiCmd, SCMD_TRANSFER_INFO | SCMD_ENABLEDMA);
- scState = SCS_DMAING;
- return SR_IOST_GOOD;
- }
-
- /*
- * Terminate a DMA, including FIFO flush if necessary. Returns number of
- * bytes transferred.
- */
- - (unsigned)dmaTerminate
- {
- unsigned char fifoDepth = 0;
- unsigned cmd;
- int tries;
- unsigned status;
- unsigned bytesXfrd;
- unsigned scsiXfrCnt;
- unsigned value;
-
- ASSERT(activeCmd != NULL);
-
- /*
- * Get resid count from SCSI block.
- */
- scsiXfrCnt = READ_REG(currXfrCntLow);
- value = READ_REG(currXfrCntMid);
- scsiXfrCnt += (value << 8);
- value = READ_REG(currXfrCntHi);
- scsiXfrCnt += (value << 16);
-
- fifoDepth = READ_REG(currFifoState) & FS_FIFO_LEVEL_MASK;
- if((activeCmd->scsiReq->read) && (scsiXfrCnt != 0)) {
- /*
- * Make sure SCSI fifo is empty. The manual says we
- * might have to wait a while.
- */
- if(fifoDepth) {
- IODelay(1000);
- fifoDepth = READ_REG(currFifoState) &
- FS_FIFO_LEVEL_MASK;
- switch(fifoDepth) {
- case 0:
- ddm_dma("dmaTerminate: fifo cleared\n",
- 1,2,3,4,5);
- break; // normal, OK
- case 1:
- IOLog("%s: Odd Byte Disconnect on target %d\n",
- [self name],
- activeCmd->scsiReq->target);
- break;
- default:
- IOLog("%s: SCSI FIFO hung\n", [self name]);
- break;
-
- /*
- * I'm not sure what to do about these
- * errors...
- */
- }
- }
- if(activeCmd->scsiReq->read) {
- cmd = DC_CMD_BLAST | DC_MDL | DC_DIR_READ;
- }
- else {
- cmd = DC_CMD_BLAST | DC_MDL | DC_DIR_WRITE;
- }
- ddm_dma(" ...sending DMA blast\n", 1,2,3,4,5);
- WRITE_REGL(dmaCommand, cmd);
- #if WRITE_DMA_COMMAND_TWICE
- WRITE_REGL(dmaCommand, cmd);
- #endif WRITE_DMA_COMMAND_TWICE
-
- /*
- * Unfortunately, we have to poll for this one. No interrupt.
- * FIXME - documentation is unclear on this. 6.7.6, the
- * description of dmaStatus, says DS_BLAST_COMPLETE is only
- * complete for "SCSI Disconnect and Reselect Operation".
- * That doesn't make a whole lot of sense to me...
- */
- for(tries=0; tries<500; tries++) {
- status = READ_REGL(dmaStatus);
- if(status & DS_BLAST_COMPLETE) {
- break;
- }
- IODelay(100);
- }
-
- ddm_dma("DMA blast : tries = %d fifoDepth = %d\n",
- tries, fifoDepth, 3,4,5);
- }
-
- /*
- * Obtain number of bytes transferred.
- */
- bytesXfrd = activeCmd->currentByteCount -
- (scsiXfrCnt + fifoDepth);
- ddm_chip("dmaTerminate: currentByteCount 0x%x, bytesXfrd 0x%x\n",
- activeCmd->currentByteCount, bytesXfrd, 3,4,5);
- #if 0
- {
- unsigned char *vp = activeCmd->buffer;
-
- ddm_init("ddmPhys = %02x %02x %02x %02x %02x\n",
- ddmPhys[0], ddmPhys[1], ddmPhys[2], ddmPhys[3], ddmPhys[4]);
- ddm_init(" %02x %02x %02x %02x %02x\n",
- ddmPhys[5], ddmPhys[6], ddmPhys[7], ddmPhys[8], ddmPhys[9]);
- ddm_init("virt = %02x %02x %02x %02x %02x\n",
- vp[0], vp[1], vp[2], vp[3], vp[4]);
- ddm_init(" %02x %02x %02x %02x %02x\n",
- vp[5], vp[6], vp[7], vp[8], vp[9]);
- }
- #endif 0
- [self dmaIdle];
- return bytesXfrd;
- }
-
- @end
-
- #if TEST_DEBUG
-
- /*
- * Do some simple I/O before IODisk starts probing us.
- */
- int loopTest = 0;
- int target = 0;
-
- #define DO_INIT_SLEEP 0
- #define DO_TUR 1
- #define DO_READ 1
- #define TEST_READ_SIZE 1 // in sectors
- #define DO_TEST_ALIGN 0
- #define TEST_DISCONNECT 1
-
- static void testDebug(id driver)
- {
- IOSCSIRequest scsiReq;
- sc_status_t srtn;
- unsigned char *rbuf;
- int block = 100000;
-
- ddm_init("testDebug\n", 1,2,3,4,5);
- if(DO_INIT_SLEEP) {
- IOLog("Sleeping for 10 seconds for DDM view\n");
- IOSleep(10000);
- }
- if(DO_READ) {
- if(DO_TEST_ALIGN) {
- rbuf = IOMallocLow(TEST_READ_SIZE * 512);
- if(rbuf == NULL) {
- IOLog("IOMallocLow returned NULL!\n");
- rbuf = IOMalloc(TEST_READ_SIZE * 512);
- }
- }
- else {
- rbuf = IOMalloc(TEST_READ_SIZE * 512);
- }
- }
- do {
- if(DO_TUR) {
- bzero(&scsiReq, sizeof(IOSCSIRequest));
- scsiReq.target = target;
- scsiReq.cmdQueueDisable = 1;
-
- /*
- * cdb = all zeroes = test unit ready
- */
- scsiReq.timeoutLength = 4;
- srtn = [driver executeRequest:&scsiReq
- buffer:NULL
- client:(vm_task_t)0];
- IOLog("testDebug: TUR result = %s\n",
- IOFindNameForValue(scsiReq.driverStatus,
- IOScStatusStrings));
- }
-
- if(DO_READ) {
- cdb_6_t *cdbp = &scsiReq.cdb.cdb_c6;
- unsigned i;
-
- bzero(&scsiReq, sizeof(IOSCSIRequest));
- scsiReq.target = target;
- scsiReq.cmdQueueDisable = 1;
- scsiReq.disconnect = TEST_DISCONNECT;
- for(i=0; i<(TEST_READ_SIZE * 512); i++) {
- rbuf[i] = i;
- }
- cdbp->c6_opcode = 8;
- cdbp->c6_len = TEST_READ_SIZE;
- cdbp->c6_lba0 = block;
- /*
- * Force a disconnect eventually
- */
- if(block == 0) {
- block = 100000;
- }
- else {
- block = 0;
- }
- scsiReq.maxTransfer = TEST_READ_SIZE * 512;
- scsiReq.read = YES;
- scsiReq.timeoutLength = 10;
- srtn = [driver executeRequest:&scsiReq
- buffer:rbuf
- client:IOVmTaskSelf()];
- if(scsiReq.driverStatus == 0) {
- ddm_init("rbuf = %02x %02x %02x %02x %02x\n",
- rbuf[0],rbuf[1],rbuf[2],rbuf[3],rbuf[4]);
- ddm_init(" %02x %02x %02x %02x %02x\n",
- rbuf[5],rbuf[6],rbuf[7],rbuf[8],rbuf[9]);
- }
- IOLog("testDebug: Read result = %s\n",
- IOFindNameForValue(scsiReq.driverStatus,
- IOScStatusStrings));
- }
- IOSleep(5000);
- } while(loopTest);
-
- }
-
- #endif TEST_DEBUG
-
- #if TEST_IPL_BUG
-
- #define IPL_TEST_LOOPS 1000000 // # of loops
- #define IPL_TEST_TIME 0 // us delay per loop
-
- static void testIplBug() {
- int loopNum;
- ns_time_t curTime, lastTime;
- unsigned usTime;
-
- IOGetTimestamp(&lastTime);
- for(loopNum=0; loopNum<IPL_TEST_LOOPS; loopNum++) {
- if(IPL_TEST_TIME) {
- IODelay(IPL_TEST_TIME);
- }
- IOGetTimestamp(&curTime);
- usTime = (unsigned)((curTime - lastTime) / 1000ULL);
- if(usTime > 1000) {
- ddm_intr("usTime %d loopNum %d curTime 0x%x lastTime "
- "0x%x\n",
- usTime, loopNum,
- (unsigned)(curTime & 0xffffffffULL),
- (unsigned)(lastTime & 0xffffffffULL), 5);
- }
- lastTime = curTime;
-
- }
- ddm_intr("testIplBug complete; IPL_TEST_TIME %d\n", IPL_TEST_TIME,
- 2,3,4,5);
-
- /*
- * Calibrate the IODelay call...
- *
- for(loopNum=0; loopNum<500; loopNum++) {
- IODelay(IPL_TEST_TIME);
- ddm_intr("IODelay(%d) calibration\n", IPL_TEST_TIME, 2,3,4,5);
- }
- */
- }
-
- #endif TEST_IPL_BUG
-