Source to bsd/dev/ppc/drvSymbios8xx/Sym8xxInit.m


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  This file contains Original Code and/or Modifications of
 * Original Code as defined in and that are subject to the Apple Public
 * Source License Version 1.0 (the 'License').  You may not use this file
 * except in compliance with the License.  Please obtain a copy of the
 * License at http://www.apple.com/publicsource and read it before using
 * this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License."
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

/* Sym8xxInit.m created by russb2 on Sat 30-May-1998 */

/*-----------------------------------------------------------------------------*
 * This module contains initialization routines for the driver.
 *
 * Driver initialization consists of:
 * 
 * - Doing PCI bus initialization for the script engine PCI device.
 * - Setting up shared communication areas in system memory between the script
 *   and the driver.
 * - Copying the script program into the script engine on-board ram, applying 
 *   script relocation fixups as required.
 * - Setting the initial register values for the script engine.
 * - Setting up driver related storage and interfacing with driverKit.
 *
 *-----------------------------------------------------------------------------*/

/*
 * This define causes Sym8xxScript.h to include the script instructions and
 * relocation tables. Normally without this define we only will get #define
 * values for interfacing with the script.
 */
#define INCL_SCRIPT_TEXT

#import "Sym8xxController.h"

/*-----------------------------------------------------------------------------*
 * This structure contains most of the inital register settings for
 * the script engine. See Sym8xxRegs.h for the actual initialization
 * values.
 *
 *-----------------------------------------------------------------------------*/
typedef struct ChipInitRegs
{
    u_int32_t		regNum;
    u_int32_t		regSize;
    u_int32_t		regValue;

} ChipInitRegs;

static ChipInitRegs 	Sym8xxInitRegs[] =
{
	{ SCNTL0,	SCNTL0_SIZE,	SCNTL0_INIT 	},
	{ SCNTL1,	SCNTL1_SIZE,    SCNTL1_INIT	},
        { SCNTL2,	SCNTL2_SIZE,	SCNTL2_INIT	},
        { SCNTL3,     	SCNTL3_SIZE,	SCNTL3_INIT_875	},
        { SXFER,	SXFER_SIZE, 	SXFER_INIT	},
        { SDID,		SDID_SIZE,	SDID_INIT	},
        { GPREG,	GPREG_SIZE,	GPREG_INIT	},
        { SFBR,		SFBR_SIZE,	SFBR_INIT	},
        { SOCL, 	SOCL_SIZE,	SOCL_INIT	},
        { DSA,		DSA_SIZE,	DSA_INIT	},
        { ISTAT,	ISTAT_SIZE,	ISTAT_INIT	},
        { TEMP,		TEMP_SIZE,	TEMP_INIT	},
        { CTEST0,	CTEST0_SIZE,	CTEST0_INIT	},
        { CTEST3,	CTEST3_SIZE,	CTEST3_INIT_A	},
        { CTEST4,	CTEST4_SIZE,	CTEST4_INIT	},
        { CTEST5,	CTEST5_SIZE,	CTEST5_INIT_A_revB},
        { DBC,		DBC_SIZE,	DBC_INIT	},
        { DCMD,		DCMD_SIZE,	DCMD_INIT	},
        { DNAD,		DNAD_SIZE,	DNAD_INIT	},
	{ DSPS,		DSPS_SIZE,	DSPS_INIT	},
	{ SCRATCHA,	SCRATCHA_SIZE,	SCRATCHA_INIT	},
        { DMODE,	DMODE_SIZE,	DMODE_INIT_A	},
        { DIEN,		DIEN_SIZE,	DIEN_INIT	},
        { DWT,		DWT_SIZE,	DWT_INIT	},
        { DCNTL,	DCNTL_SIZE,	DCNTL_INIT_A 	},
        { SIEN,		SIEN_SIZE,	SIEN_INIT	},
        { SLPAR,	SLPAR_SIZE,	SLPAR_INIT	},
        { MACNTL,	MACNTL_SIZE,	MACNTL_INIT	},
        { GPCNTL,	GPCNTL_SIZE,	GPCNTL_INIT	},
        { STIME0,	STIME0_SIZE,	STIME0_INIT	},
        { STIME1,	STIME1_SIZE,	STIME1_INIT	},
        { RESPID0,	RESPID0_SIZE,   RESPID0_INIT	},
        { RESPID1,    	RESPID1_SIZE,   RESPID1_INIT	},
        { STEST2,	STEST2_SIZE,	STEST2_INIT	},
        { STEST3,	STEST3_SIZE,	STEST3_INIT	},
        { SODL,		SODL_SIZE,	SODL_INIT	},
        { SCRATCHB,	SCRATCHB_SIZE,	SCRATCHB_INIT	}
};


@implementation Sym8xxController(Init)

/*-----------------------------------------------------------------------------*
 *  Probe, configure board and init new instance.
 *
 *-----------------------------------------------------------------------------*/
+ (BOOL)probe:(IOPCIDevice *)deviceDescription
{
    Sym8xxController	*sym8xx = [self alloc];

//  call_kdp();	
    return ([sym8xx initFromDeviceDescription: deviceDescription] ? YES : NO);		
}

/*-----------------------------------------------------------------------------*
 *
 *
 *-----------------------------------------------------------------------------*/
- initFromDeviceDescription:(IOPCIDevice *) deviceDescription
{

    if ( [super initFromDeviceDescription:deviceDescription] == nil)
    {
      goto abort;
    }

    if ( [self Sym8xxInit: deviceDescription] == NO )
    {
      goto abort;
    }

    /*
     *  
     */
    interruptPortKern = IOConvertPort(	[self interruptPort],
                                        IO_KernelIOTask,
                                        IO_Kernel);

    Sym8xxTimerReq( self );

    [self resetSCSIBus];

    /*
     *  Now that everything has succeeded, enable interrupts, and go,
     */
    [self registerDevice];	
    return self;

abort:
    [self free];
    return NULL;
}

/*-----------------------------------------------------------------------------*
 * Script Initialization
 *
 *-----------------------------------------------------------------------------*/
- (BOOL) Sym8xxInit:(IOPCIDevice *)deviceDescription
{
    /*
     * Perform PCI related initialization
     */
    if ( [self Sym8xxInitPCI: (IOPCIDevice *)deviceDescription] == NO )
    { 
        return NO;
    }

    /*
     * Allocate/initialize driver resources
     */
    if ( [self Sym8xxInitVars] == NO )
    {
        return NO;
    }

    /*
     * Initialize the script engine registers
     */
    if ( [self Sym8xxInitChip] == NO )
    {
        return NO;
    }

    /* 
     * Apply fixups to script and copy script to script engine's on-board ram
     */
    if ( [self Sym8xxInitScript] == NO )
    {
        return NO;
    }

    [self enableAllInterrupts];

    /*
     * Start script execution
     */
    Sym8xxWriteRegs( chipBaseAddr, DSP, DSP_SIZE, (u_int32_t) &chipRamAddrPhys[Ent_select_phase] );

    return YES;
}

/*-----------------------------------------------------------------------------*
 * Script engine PCI initialization
 *
 * This routine determines the chip version/revision, enables the chip address
 * ranges and allocates a virtual mapping to the script engine's registers and
 * on-board ram.
 *-----------------------------------------------------------------------------*/
- (BOOL) Sym8xxInitPCI: (IOPCIDevice *)deviceDescription
{
    IORange		*ioRange;
    unsigned long	pciReg0, pciReg8;
    u_int32_t		chipRev;
    u_int32_t		pciRamSlot=-1, pciRegSlot=-1;
    u_int32_t		n;

    /*
     * Determine the number of memory ranges for the PCI device.
     * 
     * The hardware implementation may or may not have a ROM present
     * accounting for the difference in the number of ranges.
     */
    n = [deviceDescription numMemoryRanges];
    if ( !( n == 3  ||  n == 4 )  )
    {
      return NO;
    }

    /*
     * Determine the hardware version. Check the deviceID and
     * RevID in the PCI config regs.
     */
    ioRange = [deviceDescription memoryRangeList];

    [deviceDescription configReadLong:0x00 value:&pciReg0];
    [deviceDescription configReadLong:0x08 value:&pciReg8];

    chipRev = pciReg8 & 0xff;

    if ( (pciReg0 & 0xf0000) == 0xf0000 )
    {
      chipType = 0x875;
    }
    else
    {
      chipType = 0x825;
      return NO;
    }

    kprintf( "SCSI(Symbios8xx): Chip type = %04x Chip rev = %02x\n\r", chipType, chipRev );

    /*
     * Assume 80Mhz external clock rate for motherboard 875 implementations
     * and 40Mhz for others.
     */
    if ( !strcmp([deviceDescription nodeName], "apple53C8xx") )
    {
      chipClockRate = CLK_80MHz;
    }
    else
    {
      chipClockRate = CLK_40MHz;
    }

    /*
     * BUS MASTER, MEM I/O Space, MEM WR & INV
     */
    [deviceDescription configWriteLong:0x04 value:0x16];

    /*
     *  set Latency to Max , cache 32
     */
    [deviceDescription configWriteLong:0x0C value:0x2008];

    switch ( n )
    {
      case 3:
        pciRegSlot = 2;
        pciRamSlot = 1;
        break;
      case 4:
        pciRegSlot = 3;
        pciRamSlot = 2;
    }

    /*
     * get chip register block mapped into pci memory
     */
    ioRange[pciRegSlot].size = round_page(ioRange[pciRegSlot].size);
    [deviceDescription setMemoryRangeList: ioRange num: n];
    ioRange = [deviceDescription memoryRangeList];

    chipBaseAddrPhys 	= (u_int8_t *)ioRange[pciRegSlot].start;
    [self mapMemoryRange: pciRegSlot to:(vm_address_t *)&chipBaseAddr findSpace:YES cache:IO_CacheOff];

    kprintf( "SCSI(Symbios8xx): Chip Base addr = %08x(p) %08x(v)\n\r", 
	     (u_int32_t)chipBaseAddrPhys, (u_int32_t)chipBaseAddr );

    chipRamAddrPhys 	= (u_int8_t *)ioRange[pciRamSlot].start;
    [self mapMemoryRange: pciRamSlot to:(vm_address_t *)&chipRamAddr findSpace:YES cache:IO_CacheOff];

    kprintf( "SCSI(Symbios8xx): Chip Ram  addr = %08x(p) %08x(v)\n\r",  
	     (u_int32_t)chipRamAddrPhys,  (u_int32_t)chipRamAddr );

    /*
     * Setup pointers to the script engine's registers and on-board ram for
     * inspection by the kernel mini-mon utility.
     */
#if 0
    if ( !gRegs875 )
    {
        gRegs875 	= (u_int32_t)chipBaseAddr; 
        gRegs875Phys	= (u_int32_t)chipBaseAddrPhys;
        gRam875  	= (u_int32_t)chipRamAddr;
        gRam875Phys 	= (u_int32_t)chipRamAddrPhys;
    }
#endif

    return YES;
}


/*-----------------------------------------------------------------------------*
 * This routine allocates/initializes shared memory for communication between 
 * the script and the driver. In addition other driver resources semaphores, 
 * queues are initialized here.
 *
 *-----------------------------------------------------------------------------*/
- (BOOL) Sym8xxInitVars
{
    kern_return_t		kr;
    u_int32_t			i;

    kr = kmem_alloc_wired(IOVmTaskSelf(), (vm_offset_t *) &adapter, page_size );
    if ( kr != KERN_SUCCESS )
    {
        return NO;
    }
    bzero( adapter, page_size );

    /*
     * We keep two copies of the Nexus pointer array. One contains physical addresses and
     * is located in the script/driver shared storage. The other copy holds the corresponding
     * virtual addresses to the active Nexus structures and is located in the drivers instance
     * data.
     * Both tables can be accessed through indirect pointers in the script/driver communication
     * area. This is the preferred method to access these arrays.
     */ 
    adapter->nexusPtrsVirt = (Nexus **)nexusArrayVirt;
    adapter->nexusPtrsPhys = (Nexus **)adapter->nexusArrayPhys;

    for (i=0; i < MAX_SCSI_TAG; i ++ )
    {
        adapter->nexusPtrsVirt[i] = (Nexus *) -1;
        adapter->nexusPtrsPhys[i] = (Nexus *) -1;
    }

    IOPhysicalFromVirtual((vm_task_t)IOVmTaskSelf(), (vm_offset_t)adapter, (vm_offset_t *)&adapterPhys );
 
    /*
     * The script/driver communication area also contains a 16-entry table clock
     * settings for each target.
     */ 
    for (i=0; i < MAX_SCSI_TARGETS; i++ )
    {
        targets[i].targetTagSem = [[NXLock alloc] init];
        targets[i].flags        = kTFXferSyncAllowed | kTFXferWide16Allowed | kTFCmdQueueAllowed;

        adapter->targetClocks[i].scntl3Reg = SCNTL3_INIT_875;
    }

    /*
     * Allocate/init various driver semaphores
     */
    cmdQTagSem      = [[NXLock alloc] init];
    abortBdrSem     = [[NXLock alloc] init];
    resetQuiesceSem = [[NXLock alloc] init];

    
    /*
     *  Initialize the IOThread command queue.
     */
    queue_init(&srbPendingQ);
    srbPendingQLock = [[NXLock alloc] init];

    /*
     * Initialize the SRB Pool   
     */
    queue_init(&srbPool);
    srbPoolLock = [[NXLock alloc] init];

    /*
     * Reserve our host-adapter ID so the driverKit SCSI classes do not probe it.  
     */
    for ( i = 0; i < MAX_SCSI_LUNS; i++ )
    {
        [ self reserveTarget: kHostAdapterSCSIId lun: i forOwner: self ];
    }

    /*
     * Start up the SRB Pool grow thread.
     */
    srbPoolGrowLock = [[NXConditionLock alloc] initWith: kSRBGrowPoolIdle];
    IOForkThread( (IOThreadFunc) Sym8xxGrowSRBPool, (void *)self );

    return YES;
}


/*-----------------------------------------------------------------------------*
 * This routine makes a temporary copy of the script program, applies script fixups,
 * initializes the script local data table at the top of the script image, and
 * copies the modified script image to the script engine's on-board ram.
 *
 *-----------------------------------------------------------------------------*/
- (BOOL) Sym8xxInitScript
{
    u_int32_t	 	i;
    u_int32_t		scriptPgm[sizeof(BSC_SCRIPT)/sizeof(u_int32_t)];

    /*
     * Make a copy of the script
     */
    bcopy( BSC_SCRIPT, scriptPgm, sizeof(scriptPgm) );
    bzero( scriptPgm, R_ld_size );

    /*
     * Apply fixups to the script copy
     */
    for ( i=0; i < sizeof(Rel_Patches)/sizeof(u_int32_t); i++ )
    {
        scriptPgm[Rel_Patches[i]] += (u_int32_t)chipRamAddrPhys;
    }
    for ( i=0; i < sizeof(LABELPATCHES)/sizeof(u_int32_t); i++ )
    {
        scriptPgm[LABELPATCHES[i]] += (u_int32_t)chipRamAddrPhys;
    }
 
    /*
     * Initialize the script working variables with pointers to the script/driver
     * communications area.
     */
    scriptPgm[R_ld_sched_mlbx_base_adr >> 2] 	= (u_int32_t)&adapterPhys->schedMailBox;
    scriptPgm[R_ld_nexus_array_base >> 2]    	= (u_int32_t)&adapterPhys->nexusArrayPhys;
    scriptPgm[R_ld_device_table_base_adr >> 2] 	= (u_int32_t)&adapterPhys->targetClocks;

    /*
     * Load the script image into the script engine's on-board ram.
     */
    [self Sym8xxLoadScript: scriptPgm count:sizeof(scriptPgm)/sizeof(u_int32_t)];

    return YES;
}


/*-----------------------------------------------------------------------------*
 * This routine transfers the script program image into the script engine's
 * on-board ram
 *
 *-----------------------------------------------------------------------------*/
- (void) Sym8xxLoadScript:(u_int32_t *)scriptPgm count:(u_int32_t)scriptWords
{
    u_int32_t			i;
    volatile u_int32_t		*ramPtr = (volatile u_int32_t *)chipRamAddr;

    for ( i = 0; i < scriptWords; i++ )
    {
        ramPtr[i] = EndianSwap32(scriptPgm[i]);
    }
}

/*-----------------------------------------------------------------------------*
 * This routine initializes the script engine's register block.
 *
 *-----------------------------------------------------------------------------*/
- (BOOL) Sym8xxInitChip
{
    u_int32_t			i;

    /*
     * Reset the script engine
     */
    Sym8xxWriteRegs( chipBaseAddr, ISTAT, ISTAT_SIZE, RST );
    IODelay( 25 );
    Sym8xxWriteRegs( chipBaseAddr, ISTAT, ISTAT_SIZE, ISTAT_INIT );
  
    /*
     * Load our canned register values into the script engine
     */
    for ( i = 0; i < sizeof(Sym8xxInitRegs)/sizeof(ChipInitRegs); i++ )
    {
        Sym8xxWriteRegs( chipBaseAddr, Sym8xxInitRegs[i].regNum, Sym8xxInitRegs[i].regSize, Sym8xxInitRegs[i].regValue );
        IODelay( 10 );
    }

    /*
     * For hardware implementations that have a 40Mhz SCLK input, we enable the chip's on-board
     * clock doubler to bring the clock rate upto 80Mhz which is required for Ultra-SCSI timings.
     */
    if ( chipType == 0x875 )
    {
        if ( chipClockRate == CLK_40MHz )
        {
            /*
             *   Clock doubler setup for 875 (rev 3 and above).
             */
            /* set clock doubler enabler bit */
            Sym8xxWriteRegs( chipBaseAddr, STEST1, STEST1_SIZE, STEST1_INIT | DBLEN);
            IODelay(30);  
            /* halt scsi clock */
            Sym8xxWriteRegs( chipBaseAddr, STEST3, STEST3_SIZE, STEST3_INIT | HSC );
            IODelay(10);
            Sym8xxWriteRegs( chipBaseAddr, SCNTL3, SCNTL3_SIZE, SCNTL3_INIT_875);
            IODelay(10);
            /* set clock doubler select bit */
            Sym8xxWriteRegs( chipBaseAddr, STEST1, STEST1_SIZE, STEST1_INIT | DBLEN | DBLSEL);
            IODelay(10);
            /* clear hold on scsi clock */
            Sym8xxWriteRegs( chipBaseAddr, STEST3, STEST3_SIZE, STEST3_INIT);
        }
    }
    else
    {
        Sym8xxWriteRegs( chipBaseAddr, SCNTL3, SCNTL3_SIZE, SCNTL3_INIT);
    }

    /*  
     * Set our host-adapter ID in the script engine's registers
     */
    initiatorID = kHostAdapterSCSIId;

    if ( initiatorID > 7 )
    {
        Sym8xxWriteRegs( chipBaseAddr, RESPID1, RESPID1_SIZE, 1 << (initiatorID-8));
    }
    else
    {
        Sym8xxWriteRegs( chipBaseAddr, RESPID0, RESPID0_SIZE, 1 << initiatorID);
    }

    Sym8xxWriteRegs( chipBaseAddr, SCID, SCID_SIZE, SCID_INIT | initiatorID );

    return YES;
}


@end