/*
 *  AppleDallasDriver.cpp
 *  AppleDallasDriver
 *
 *  Created by Keith Cox on Tue Jul 17 2001.
 *  Copyright (c) 2001 Apple Computer, Inc. All rights reserved.
 *
 */

#include <IOKit/IOLib.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/IORegistryEntry.h>

#include "AppleDallasDriver.h"
#include "DallasROM.h"
#include "AudioHardwareUtilities.h"
extern "C" {
#include <pexpert/pexpert.h>
}

// Define my superclass
#define super IOService

// REQUIRED! This macro defines the class's constructors, destructors,
// and several other methods I/O Kit requires.  Do NOT use super as the
// second parameter.  You must use the literal name of the superclass.
OSDefineMetaClassAndStructors(AppleDallasDriver, IOService)

//================================================================================
//	The bus master (this driver) asserts a reset pulse.  The target device
//	responds with a "Presence" pulse prior to expiration of tPDH max.
//
//			                   >|         tRSTH              |<
//	Vpullup	_____                 _______              _______
//	Vih MIN	     \               /       \            /       \
//			      \             /         \          /         \
//	Vil MAX	       \           /           \        /           \
//	0v		        \_________/             \______/             \
//			                    >| tPDH  |<
//			                 >|tR|<      |
//			       >|  tRSTL  |<        >|  tPDL  |<
//
//	Where:	15 µS ² tPDH < 60 µS
//			60 µS ² tPDL < 240 µS
//			480 µS ² tRSTH
//			480 µS ² tRSTL < °
//			tRSTL + tR + tRSTH < 960 µS
//
bool ROMReset (UInt8 *gpioPtr)
{
    AbsoluteTime start, now, finish;
    UInt64       ns;
    UInt8	 bit;
	UInt8		outputZero, outputTristate;
    bool         correctTiming;
    bool         bitEdge;
    int          errorCount = 0;
    bool         res;    
	
	outputZero      = (*gpioPtr & ( dualEdge << intEdgeSEL )) | ( gpioDDR_OUTPUT << gpioDDR );	// set output select0, altoe=0, ddir=1, output value = 0
																								// save bit 7
	outputTristate  = (*gpioPtr & ( dualEdge << intEdgeSEL )) | ( gpioDDR_INPUT << gpioDDR );	// output bit = 0, but output tristated to get a 1

    res = TRUE;
    // Make sure the ROM is present.  The ROM's one-wire-bus is pulled low if
	//	no rom is present through switching of the speaker jack connector and
	//	pulled high if the ROM is present.  The ROM itself will not pull the
	//	bus low unless in response to a reset or data query.
    *gpioPtr = outputTristate; OSSynchronizeIO();
    if ( ( *gpioPtr & ( 1 << gpioPIN_RO ) ) == 0 ) {
        IOSleep(1);
        FailWithAction ((*gpioPtr & ( 1 << gpioPIN_RO )) == 0, debugIOLog("AppleDallasDriver::ROMReset No speaker ROM detected\n"), exit);
    }

    correctTiming = FALSE;
    while (!correctTiming)
    {
        debugIOLog("[DALLAS] ROM Reset\n");
        correctTiming = TRUE;
        
        clock_get_uptime(&start);
        *gpioPtr = outputZero; OSSynchronizeIO();
        IODelay(kROMResetPulseMin);
        *gpioPtr = outputTristate; OSSynchronizeIO();
        
        ns = kROMResetPulseMax * kNANOSECONDS_PER_MICROSECOND;
        nanoseconds_to_absolutetime(ns, &finish);
        ADD_ABSOLUTETIME(&finish, &start);
        
		// Wait for rising edge of reset pulse or HIGH level for quiescent bus state
        bitEdge = FALSE;  
        while (!bitEdge && correctTiming)
        {
            bit = *gpioPtr & ( 1 << gpioPIN_RO );
            clock_get_uptime(&now);
            
            if (CMP_ABSOLUTETIME(&now, &finish) > 0) {
                correctTiming = FALSE;
                debugIOLog("[DALLAS] ROM Reset: Failed Reset Rising Edge\n");
            } else if (bit) {
                bitEdge = TRUE;
            } else {
                IODelay(1);
            }
        }
        
		//	BEGIN the RESET PULSE
        ns = kROMPresenceDelayMax * kNANOSECONDS_PER_MICROSECOND;
        nanoseconds_to_absolutetime(ns, &finish);
        ADD_ABSOLUTETIME(&finish, &now);
        
        bitEdge = FALSE;  // Wait for falling edge of presence pulse
        while (!bitEdge && correctTiming)
        {
            bit = *gpioPtr & ( 1 << gpioPIN_RO );
            clock_get_uptime(&now);
            
            if (CMP_ABSOLUTETIME(&now, &finish)>0) {
                correctTiming = FALSE;
                IOSleep(1);
				debugIOLog("AppleDallasDriver::ROMReset  Failed to detect Presence Pulse\n");
            } else if (!bit) {
                bitEdge = TRUE;
            } else {
                IODelay(1);
            }
        }
        
		//	BEGIN detection of the PRESENCE PULSE
        ns = kROMPresencePulseMax * kNANOSECONDS_PER_MICROSECOND;
        nanoseconds_to_absolutetime(ns, &finish);
        ADD_ABSOLUTETIME(&finish, &now);
        
        bitEdge = FALSE;  // Wait for rising edge of presence pulse
        while (!bitEdge && correctTiming)
        {
            bit = *gpioPtr & ( 1 << gpioPIN_RO );
            clock_get_uptime(&now);
            
            if (CMP_ABSOLUTETIME(&now, &finish)>0) {
                correctTiming = FALSE;
//                debugIOLog("[DALLAS] ROM Reset: Presence Pulse too long\n");
            } else if (bit) {
                bitEdge = TRUE;
            } else {
                IODelay(1);
            }
        }
        
        if (!correctTiming)
        {
            FailWithAction(++errorCount > 10, debugIOLog("[DALLAS] ROM Reset: Too many failures, bailing out\n"), exit);
        }
        IOSleep(1);  // Give up the cpu after all attempts
    }

    res = FALSE;
    
exit:
    return res;
}

//================================================================================
//	Write-1 Time Slot:
//	Vpullup	_____                 ____________________________
//	Vih MIN	     \               /                            \
//			      \             /                              \
//	Vil MAX	       \           /                                \
//	0v		        \_________/                                  \
//
//			       >|   tLOW1    |<
//			       >|           tLOW0            |<  >| tREC |<
//			       >|            tSLOT                |<
//
//	Where:	1 µS ² tLOW1 < 15 µS
//			60 µS ² tSLOT ² 120 µS
//			1 µS ² tREC < °
//
//	Write-0 Time Slot:
//	Vpullup	_____                                      _______
//	Vih MIN	     \                                    /       \
//			      \                                  /         \
//	Vil MAX	       \                                /           \
//	0v		        \______________________________/             \
//
//			       >|   tLOW1    |<
//			       >|           tLOW0            |<  >| tREC |<
//			       >|            tSLOT                |<
//
//	Where:	60 µS ² tLOW0 < 120 µS
//			60 µS ² tSLOT ² 120 µS
//			1 µS ² tREC < °
//
//	Data is transacted lsb first.  Returns TRUE if failed!
//
//	Althought the tLOW1 is specified as 1µS minimum, it is unlikely that our hardware
//	will propagate such a short duration pulse so the pulse is done at half the maximum.
//	EMI filtering on the speaker jack can impact the timing such that an RC charge curve
//	occurs as the signal is driven low and released high.  This curve causes the actual
//	time period that the signal is below the minimum input voltage (i.e. Vil MAX) to be
//	reduced and may result in timing less than the minimum allowed for the one-wire-bus
//	protocol.  Using longer timing, which does not violate the maximum timing, allows 
//	the charge curve to progress deeper toward the desired bus state and will result
//	in timing that does not violate the minimum timing for the signal assertion.  This
//	rule has been applied throughout this driver.  rbm 16 Oct 2002	[3053696]
bool ROMSendByte ( UInt8 *gpioPtr, UInt8 theByte, UInt8 msgRefCon )
{
    AbsoluteTime	start, now, finish, tLOW;
    UInt64			ns;
    UInt8			bit;
	UInt8			outputZero, outputTristate;
    bool			correctTiming;
    bool			bitEdge;
    bool			res;
    int				bitIndex;
    
    debug2IOLog("[DALLAS] ROM Send 0x%02X\n", theByte);
	
	outputZero      = (*gpioPtr & ( dualEdge << intEdgeSEL )) | ( gpioDDR_OUTPUT << gpioDDR );	// set output select0, altoe=0, ddir=1, output value = 0
																								// save bit 7
	outputTristate  = (*gpioPtr & ( dualEdge << intEdgeSEL )) | ( gpioDDR_INPUT << gpioDDR );	// output bit = 0, but output tristated to get a 1

    res = FALSE;		// assume success
    for ( bitIndex = 0; bitIndex < kBITS_PER_BYTE; bitIndex++)
    {
		clock_get_uptime(&start);
		ns = kTSLOT_maximum * kNANOSECONDS_PER_MICROSECOND;				//	tSLOT 120 µS limit
		nanoseconds_to_absolutetime(ns, &finish);
		ADD_ABSOLUTETIME(&finish, &start);

        correctTiming = TRUE;
        if ( theByte & ( 1 << bitIndex ) ) {							// bit=1
            ns = kTLOW1_maximum * kNANOSECONDS_PER_MICROSECOND;			//	tLOW1 15 µS limit
            nanoseconds_to_absolutetime(ns, &tLOW);
            ADD_ABSOLUTETIME(&tLOW, &start);

            *gpioPtr = outputZero; OSSynchronizeIO();					//	assert to '0' bus level
            IODelay( kTLOW1_maximum / 2 );								//	1 µS ² tLOW1 < 15 µS	rbm 16 Oct 2002	[3053696]
            *gpioPtr = outputTristate; OSSynchronizeIO();				//	release to '1' bus level
                        
            bitEdge = FALSE;  // Wait for rising edge of bit
            while (!bitEdge && correctTiming)
            {
                bit = *gpioPtr & ( 1 << gpioPIN_RO );
                clock_get_uptime(&now);
                
				//	Check to see that bus released within tLOW1 maximum limit of 15 µS
                if ( CMP_ABSOLUTETIME ( &now, &tLOW ) > 0 ) {
                    correctTiming = FALSE;
                    debug4IOLog( "... ROM Send Byte: theByte %X, Failed Bit %d Rising Edge, msgRefCon %d\n", 
						(unsigned int)theByte, 
						(unsigned int)bitIndex, 
						(unsigned int)msgRefCon );
                } else if (bit) {
                    bitEdge = TRUE;
                } else {
                    IODelay(1);
                }
            }
        } else {														// bit = 0
            ns = kTLOW0_maximum * kNANOSECONDS_PER_MICROSECOND;			//	60 µS ² tLOW0 ² tSLOT ² 120 µS
            nanoseconds_to_absolutetime(ns, &tLOW);
            ADD_ABSOLUTETIME(&tLOW, &start);
            
            *gpioPtr = outputZero; OSSynchronizeIO();					//	assert to '0' bus level
            IODelay( ( kTLOW0_minimum + kTLOW0_maximum ) / 2 );			//	use 90 µS	rbm 16 Oct 2002	[3053696]
            *gpioPtr = outputTristate; OSSynchronizeIO();				//	release to '1' bus level
            
            bitEdge = FALSE;  // Wait for rising edge of bit
            while (!bitEdge && correctTiming)
            {
                bit = *gpioPtr & ( 1 << gpioPIN_RO );
                clock_get_uptime(&now);
                
                if (CMP_ABSOLUTETIME(&now, &tLOW)>0) {
                    correctTiming = FALSE;
                    debug4IOLog( "... ROM Send Byte: theByte %X, Failed Bit %d Rising Edge, msgRefCon %d\n", 
						(unsigned int)theByte, 
						(unsigned int)bitIndex, 
						(unsigned int)msgRefCon );
                } else if (bit) {
                    bitEdge = TRUE;
                } else {
                    IODelay(1);
                }
            }
        }

		IODelay ( kTSLOT_minimum + kTREC );									//	make sure remaining tSLOT + tREC is greater than 611 µS
        
        if (!correctTiming)
            res = TRUE;
	}

	IOSleep(1);					// Give up CPU every byte
	if ( res ) { debugIOLog ( "ROMSendByte FAILED!\n" ); }
    return res;
}

//================================================================================
//	Read Data Time Slot:
//	Vpullup	_____                 ____________________________
//	Vih MIN	     \               /                    /       \
//			      \             /                    /         \
//	Vil MAX	       \           /                    /           \
//	0v		        \_________/___________________ /             \
//
//			       >|            tSLOT                |<
//			       >|   tLOWR    |<    >| tRELEASE |<
//			       >|        tRDV       |<           >| tREC |<
//
//	Where:	1 µS ² tLOWR < 15 µS
//			60 µS ² tSLOT ² 120 µS
//			1 µS ² tRELEASE ² 45 µS
//			15 µS = tRDV
//			1 µS ² tREC < °
//
//	Data is transacted lsb first.
bool ROMReadByte (UInt8 *gpioPtr, UInt8* theByte)
{
    AbsoluteTime	start, finish, tRdv, after;
    UInt64			ns;
    UInt8			bit, failedTRdv, failedTRelease, outputZero, outputTristate, theValue = 0;
    bool			correctTiming, bitEdge, res;
    int				bitIndex;
	
	outputZero      = (*gpioPtr & ( dualEdge << intEdgeSEL )) | ( gpioDDR_OUTPUT << gpioDDR );	// set output select0, altoe=0, ddir=1, output value = 0
																								// save bit 7
	outputTristate  = (*gpioPtr & ( dualEdge << intEdgeSEL )) | ( gpioDDR_INPUT << gpioDDR );	// output bit = 0, but output tristated to get a 1

    res = FALSE;		// assume success
	failedTRelease = failedTRdv = 0;
    for ( bitIndex = 0; bitIndex < kBITS_PER_BYTE; bitIndex++)
    {
        correctTiming = TRUE;
        
		//	There also was a timing violation in the ROMReadByte method where 1 µS < tSU was used to 
		//	assert the bus to a low state at the start of reading a bit cell.  The tSU timing parameter 
		//	actualy refers to how fast the device asserts the bus after sensing a low on the bus and 
		//	does not refer to the timing that the master should continue to assert the bus to a low state. 
		//	The bus master should continue to drive the bus low up to tLOWR maximum or < 15 µS.  The 
		//	same concerns about RC charge currents applies here so I've lengthened the timing to half 
		//	the maximum or 7.5 µS.  Care must be taken to make sure that tROMSlot timing is not measured
		//	from any signal element other than the initial falling edge (i.e. '0' bus level state) to
		//	avoid accumulating timing error over multiple bit cells.		rbm 16 Oct 2002	[3053696]
		clock_get_uptime ( &start );
        ns = kTSLOT_maximum * kNANOSECONDS_PER_MICROSECOND;			//	tSlot begins at start of bit cell	rbm 16 Oct 2002	[3053696]
        nanoseconds_to_absolutetime ( ns, &finish );
        ADD_ABSOLUTETIME(&finish, &start);
		ns = ( kTRDV + 1 ) * kNANOSECONDS_PER_MICROSECOND;
        nanoseconds_to_absolutetime ( ns, &tRdv );
        ADD_ABSOLUTETIME(&tRdv, &start);							//	masterSampleWindow indicates tRDV relative to start of bit cell		
		
        *gpioPtr = outputZero; OSSynchronizeIO();					//	assert to '0' bus level starts the bit cell
        IODelay( kTLOWR_maximum / 3 );								//	1 µS ² tLOWR < 15 µS	rbm 16 Oct 2002	[3053696]
        *gpioPtr = outputTristate; OSSynchronizeIO();				//	release to '1' bus level
        
		//	Once the bus has been released from the '0' state at the beginning of the bit cell, the
		//	device will have asserted asserted a '0' or released to a '1' in parallel with the master
		//	'0' assertion on the bus and within 1 µS of the master asserting '0'.  The data is then
		//	read between tLOWR maximum and tRDV (i.e. MASTER SAMPLING WINDOW).  The tRDV indicates
		//	the point where the data is to be sampled (see Dallas application note 126: ReadX function).
		//	rbm	16 Oct 2002	[3053696]
		
		IODelay ( kTRDV - ( kTLOWR_maximum / 3 ) - 1 );
		bit = *gpioPtr & ( 1 << gpioPIN_RO );
		clock_get_uptime(&after);
		if ( CMP_ABSOLUTETIME ( &after, &tRdv ) > 0 ) {
			correctTiming = FALSE;
			res = TRUE;
			failedTRdv |= ( 1 << bitIndex );
		}
		
		//	Now wait for release (necessary when the bit cell is a '0'.  This must occur within 45 µS
		//	of tRDV (i.e. 0 µS ² tRELEASE < 45 µS).
		if ( 0 == bit ) {
			bitEdge = FALSE;
			correctTiming = TRUE;
			while ( !bitEdge && correctTiming ) {
				if ( *gpioPtr & ( 1 << gpioPIN_RO ) ) {
					bitEdge = TRUE;
				}
				clock_get_uptime(&after);
				if ( CMP_ABSOLUTETIME ( &after, &finish ) > 0 ) {
					correctTiming = FALSE;
					res = TRUE;
					failedTRelease |= ( 1 << bitIndex );
				}
			}
		}
		IODelay( kTSLOT_minimum + kTREC );
		
		//	'bit' now contains the bit sample state within acceptable timing margins if 'correctTiming' is TRUE.
                
        theValue |= ( bit == 0 ? 0 << bitIndex : 1 << bitIndex );
    }
	if ( res ) {
		debug4IOLog("[DALLAS] ROMReadByte tRDV failed on bits %X, tRELEASE failed on bits %X, data %X\n", (unsigned int)failedTRdv, (unsigned int)failedTRelease, (unsigned int)theValue );
	}
	
    IOSleep(1);														// give up CPU every byte
	*theByte = theValue;
    debug2IOLog("[DALLAS]   ROM Read 0x%02x\n", *theByte);
	if ( res ) { debugIOLog ( "ROMReadByte FAILED!\n" ); }
    return res;
}

//================================================================================
void ROMCheckCRC(UInt8 *bROM)
{
    int index, j;
    UInt8 crc[8] = {0, 0, 0, 0, 0, 0, 0, 0};
    UInt8 currByte, newBit;
    UInt8 finalCRC;
    
    for (index=0; index<7; index++) {
        currByte = bROM[index];
        for (j=0; j<8; j++) {
            newBit = crc[7] ^ (currByte&0x01);
            crc[7] = crc[6];
            crc[6] = crc[5];
            crc[5] = crc[4] ^ newBit;
            crc[4] = crc[3] ^ newBit;
            crc[3] = crc[2];
            crc[2] = crc[1];
            crc[1] = crc[0];
            crc[0] = newBit;
            currByte >>= 1;
        }
    }
    
    finalCRC = 0;
    for (index=0; index<8; index++) {
        finalCRC = (finalCRC << 1) | crc[index];
    }
    
    if (finalCRC != bROM[7]) {
        debug3IOLog("[DALLAS] CRC Mismatch! 0x%02x 0x%02x\n", finalCRC, bROM[7]);
    } else {
        debug2IOLog("[DALLAS] ROM CRC Match: 0x%02x\n", finalCRC);
    }
    
}

//================================================================================
IORegistryEntry * FindEntryByProperty (const IORegistryEntry * start, const char * key, const char * value) 
{
	OSIterator				*iterator;
	IORegistryEntry			*theEntry;
	IORegistryEntry			*tmpReg;
	OSData					*tmpData;

	theEntry = NULL;
	iterator = start->getChildIterator (gIODTPlane);
	FailIf (NULL == iterator, Exit);

	while (NULL == theEntry && (tmpReg = OSDynamicCast (IORegistryEntry, iterator->getNextObject ())) != NULL) {
		tmpData = OSDynamicCast (OSData, tmpReg->getProperty (key));
		if (NULL != tmpData && tmpData->isEqualTo (value, strlen (value))) {
			theEntry = tmpReg;
		}
	}

Exit:
	if (NULL != iterator) {
		iterator->release ();
	}
	return theEntry;
}

//================================================================================
bool AppleDallasDriver::init (OSDictionary *dict)
{
    bool res = super::init (dict);
    debugIOLog ("[DALLAS] AppleDallasDriver Initializing\n");
    return res;
}

//================================================================================
void AppleDallasDriver::free (void)
{
    debugIOLog ("AppleDallasDriver Freeing\n");
    CLEAN_RELEASE (gpioRegMem)
    super::free ();
}

//================================================================================
IOService *AppleDallasDriver::probe (IOService *provider, SInt32 *score)
{
    
    IOService *res = super::probe (provider, score);
    debugIOLog ("[DALLAS] AppleDallasDriver Probing\n");
    return res;
}

//================================================================================
bool AppleDallasDriver::readApplicationRegister (UInt8 *bAppReg)
{
    IOMemoryMap	*map = NULL;
    UInt8		*gpioPtr = NULL;
    int			index;
    bool		failure;
    bool		resultSuccess;
	int			retryCount;
    
    resultSuccess = FALSE;
    map = gpioRegMem->map (0);
    FailIf (!map, exit);
    gpioPtr = (UInt8*)map->getVirtualAddress ();
    debug2IOLog("[DALLAS] GPIO16 Register Value = 0x%02x\n", *gpioPtr);

    FailIf (!gpioPtr, exit);
    
    // Look for ROM present
    FailWithAction(ROMReset(gpioPtr), debugIOLog("No speaker ROM detected\n"), exit);
    
    // Read 64b Application Register
    failure = TRUE;
    debugIOLog("[DALLAS] Reading 64b Application Register\n");
	retryCount = kRetryCountSeed;
    for (index=0; index<8; index++) {
        do {
            while (failure && retryCount) {
                ROMReset(gpioPtr);
                failure = ROMSendByte(gpioPtr, kROMSkipROM, 0 );
                failure = failure || ROMSendByte(gpioPtr, kROMReadAppReg, 1 );
                failure = failure || ROMSendByte(gpioPtr, index, 2 );
				retryCount--;
            }
            failure = ROMReadByte(gpioPtr, &bAppReg[index]);
        } while (failure && retryCount);
    }
    if ( !failure ) {
		resultSuccess = TRUE;
	}
	
exit:
    return resultSuccess;
}

//=========================================================================================
//	Dallas 2430A ROM EEPROM read transaction:
//
//	BUS PHASE			VALUE	ACTION					DESCRIPTION
//	--------------		----	---------------------	-----------------------------------
//	RESET				...		...						...
//	PRESENCE PULSE		...		...						...
//	SEND BYTE			0xCC	SKIP ROM				Bypass lasered ROM
//	SEND BYTE			0xF0	READ MEMORY				Copy EEPROM to Scratchpad (Note 1)
//	RESET				...		...						...
//	PRESENCE PULSE		...		...						...
//	SEND BYTE			0xCC	SKIP ROM				Bypass lasered ROM
//	SEND BYTE			0xAA	READ SCRATCHPAD			Copy Scratchpad to CPU
//	SEND BYTE			0x00	SCRATCHPAD ADDRESS		Beginning address modulus 32
//	READ BYTE			0x__	Read 'deviceFamily'		speaker ID field
//	READ BYTE			0x__	Read 'deviceType'		speaker ID field
//	READ BYTE			0x__	Read 'deviceSubtype'	speaker ID field	(Note 2)
//	READ BYTE			0x__	Read 'deviceReserved'	speaker ID field	(Note 2)
//	
//	Note 1:		No address is conveyed during the READ MEMORY command which results
//				in copying the entire EEPROM to the scratchpad memory.
//	Note 2:		Only the 'deviceFamily' and 'deviceType' fields are used.  The
//				'deviceSubtype' and 'deviceReserved' fields have not been defined
//				for any shipping product and are intended for future expansion only.
//				Reading 'deviceSubtype' and 'deviceReserved' fields is optional.
//
//	Restructured the AppleDallasDriver::readDataROM method to implement a state machine with a 
//	conditional compile flag that allows the retry method to be implemented with more flexibility  
//	in recovery options.  The Dallas ROM is segmented into four sections which consist of a  
//	lasered ROM (i.e. serial number), application register, scratchpad memory and EEPROM.  The  
//	speaker data is held in the EEPROM but cannot be accessed directly.  The driver must transfer  
//	the data from the EEPROM to the scratchpad memory and then read the scratchpad memory.  This  
//	takes two separate transactions.  The state machine was implemented to avoid increasing nesting  
//	of conditional execution statements in providing for recovery mechanisms that allow failed data  
//	values from the second transaction to provide for recovery by initiating a retry at the transfer  
//	of the EEPROM data to scratchpad memory.  If the scratchpad memory had incorrect data then  
//	retrying the scratchpad transaction would only result in fetching incorrect data on each retry.
//	rbm 16 Oct 2002	[3053696]
bool AppleDallasDriver::readDataROM (UInt8 *bEEPROM,int dallasAddress, int size)
{
    IOMemoryMap     *map = NULL;
    UInt8           tempData, *gpioPtr = NULL;
    int				index;
    bool			resultSuccess;
	int				retryCount, stateMachine;
    
	debug4IOLog ( "+ AppleDallasDriver::readDataROM ( %X, %X, %X )\n", (unsigned int)bEEPROM, (unsigned int)dallasAddress, (unsigned int)size );
    resultSuccess = FALSE;			//	assume that it failed
	
	FailIf ( NULL == bEEPROM, exit );
	((DallasIDPtr)bEEPROM)->deviceFamily = kDeviceFamilyUndefined;
	((DallasIDPtr)bEEPROM)->deviceType = kDallasSpeakerRESERVED;
	((DallasIDPtr)bEEPROM)->deviceSubType = 0;
	((DallasIDPtr)bEEPROM)->deviceReserved = 0;

    map = gpioRegMem->map (0);
    FailIf (!map, exit);
    gpioPtr = (UInt8*)map->getVirtualAddress ();
    debug2IOLog( "[DALLAS] GPIO16 Register Value = 0x%02x\n", *gpioPtr );
    FailIf (!gpioPtr, exit);
	
	stateMachine = kSTATE_RESET_READ_MEMORY;
	retryCount = kRetryCountSeed;
	while( ( kSTATE_COMPLETED != stateMachine ) && ( 0 != retryCount ) ) {
		switch ( stateMachine ) {
			case kSTATE_RESET_READ_MEMORY:
				if ( ROMReset ( gpioPtr ) ) {
					debug3IOLog ( "... failed at kSTATE_RESET_READ_MEMORY %d, retryCount %d\n", (unsigned int)stateMachine, (unsigned int)retryCount );
					stateMachine = kSTATE_RESET_READ_MEMORY;
					if ( retryCount ) { retryCount--; }
				} else {
					stateMachine = kSTATE_CMD_SKIPROM_READ_MEMORY;
				}
				break;
			case kSTATE_CMD_SKIPROM_READ_MEMORY:
				if ( ROMSendByte ( gpioPtr, kROMSkipROM, stateMachine ) ) {
					debug3IOLog ( "... failed at kSTATE_CMD_SKIPROM_READ_MEMORY %d, retryCount %d\n", (unsigned int)stateMachine, (unsigned int)retryCount );
					stateMachine = kSTATE_RESET_READ_MEMORY;
					if ( retryCount ) { retryCount--; }
				} else {
					stateMachine = kSTATE_CMD_READ_MEMORY;
				}
				break;
			case kSTATE_CMD_READ_MEMORY:
				if ( ROMSendByte ( gpioPtr, kROMReadMemory, stateMachine ) ) {
					debug3IOLog ( "... failed at kSTATE_CMD_READ_MEMORY %d, retryCount %d\n", (unsigned int)stateMachine, (unsigned int)retryCount );
					stateMachine = kSTATE_RESET_READ_MEMORY;
					if ( retryCount ) { retryCount--; }
				} else {
					if ( 0 != dallasAddress ) {
						stateMachine = kSTATE_READ_MEMORY_ADDRESS;
					} else {
						stateMachine = kUSE_DESCRETE_BYTE_TRANSFER ? kSTATE_READ_MEMORY_ADDRESS : kSTATE_RESET_READ_SCRATCHPAD;
					}
				}
				break;
			case kSTATE_READ_MEMORY_ADDRESS:
				if ( ROMSendByte ( gpioPtr, dallasAddress, stateMachine ) ) {					//	ROM address is 0x00
					debug3IOLog ( "... failed at kSTATE_READ_MEMORY_ADDRESS %d, retryCount %d\n", (unsigned int)stateMachine, (unsigned int)retryCount );
					stateMachine = kSTATE_RESET_READ_MEMORY;
					if ( retryCount ) { retryCount--; }
				} else {
					stateMachine = kSTATE_READ_MEMORY_DATA;
					index = 0;
				}
				break;
			case kSTATE_READ_MEMORY_DATA:
				if ( ROMReadByte ( gpioPtr, &tempData ) ) {
					debug4IOLog ( "... failed at kSTATE_READ_MEMORY_DATA %d, retryCount %d, byte %d\n", (unsigned int)stateMachine, (unsigned int)retryCount, (unsigned int)index );
					stateMachine = kSTATE_RESET_READ_MEMORY;
					if ( retryCount ) { retryCount--; }
				} else {
					index++;
					if ( index == size ) { stateMachine = kSTATE_RESET_READ_SCRATCHPAD; }
				}
				break;
			case kSTATE_RESET_READ_SCRATCHPAD:
				if ( ROMReset ( gpioPtr ) ) {
					debug3IOLog ( "... failed at kSTATE_RESET_READ_SCRATCHPAD %d, retryCount %d\n", (unsigned int)stateMachine, (unsigned int)retryCount );
					stateMachine = kSCRATCHPAD_RETRY_STATE;
					if ( retryCount ) { retryCount--; }
				} else {
					stateMachine = kSTATE_CMD_SKIPROM_SCRATCHPAD;
				}
				break;
			case kSTATE_CMD_SKIPROM_SCRATCHPAD:
				if ( ROMSendByte ( gpioPtr, kROMSkipROM, stateMachine ) ) {
					debug3IOLog ( "... failed at kSTATE_CMD_SKIPROM_SCRATCHPAD %d, retryCount %d\n", (unsigned int)stateMachine, (unsigned int)retryCount );
					stateMachine = kSCRATCHPAD_RETRY_STATE;
					if ( retryCount ) { retryCount--; }
				} else {
					stateMachine = kSTATE_CMD_SCRATCHPAD;
				}
				break;
			case kSTATE_CMD_SCRATCHPAD:
				if ( ROMSendByte ( gpioPtr, kROMReadScratch, stateMachine ) ) {
					debug3IOLog ( "... failed at kSTATE_CMD_SCRATCHPAD %d, retryCount %d\n", (unsigned int)stateMachine, (unsigned int)retryCount );
					stateMachine = kSCRATCHPAD_RETRY_STATE;
					if ( retryCount ) { retryCount--; }
				} else {
					stateMachine = kSTATE_SCRATCHPAD_ADDRESS;
				}
				break;
			case kSTATE_SCRATCHPAD_ADDRESS:
				if ( ROMSendByte ( gpioPtr, dallasAddress, stateMachine ) ) {		//	ROM address is 0x00
					debug3IOLog ( "... failed at kSTATE_SCRATCHPAD_ADDRESS %d, retryCount %d\n", (unsigned int)stateMachine, (unsigned int)retryCount );
					stateMachine = kSCRATCHPAD_RETRY_STATE;
					if ( retryCount ) { retryCount--; }
				} else {
					stateMachine = kSTATE_READ_SCRATCHPAD;
					index = 0;
				}
				break;
			case kSTATE_READ_SCRATCHPAD:
				if ( ROMReadByte ( gpioPtr, &bEEPROM[index] ) ) {
					debug4IOLog ( "... failed at kSTATE_READ_SCRATCHPAD %d, retryCount %d, byte %d\n", (unsigned int)stateMachine, (unsigned int)retryCount, (unsigned int)index );
					stateMachine = kSCRATCHPAD_RETRY_STATE;							//	timing failed so retry at scratchpad transaction
					if ( retryCount ) { retryCount--; }
				} else {
					switch ( index ) {
						case 0:
							if ( kDeviceFamilySpeaker == ((DallasIDPtr)bEEPROM)->deviceFamily ) {
								index++;
								if ( index >= size ) { stateMachine = kSTATE_COMPLETED; }
							} else {
								debug2IOLog ( "... failed with bad deviceFamily data %X, resetting state machine\n", ((DallasIDPtr)bEEPROM)->deviceFamily );
								stateMachine = kSTATE_RESET_READ_MEMORY;			//	bad data then retry at copy of EEPROM to scratchpad
							}
							break;
						case 1:
							if ( 0xFF != ((DallasIDPtr)bEEPROM)->deviceSubType ) {
								index++;
								if ( index >= size ) { stateMachine = kSTATE_COMPLETED; }
							} else {
								debug2IOLog ( "... failed with bad deviceSubType data %X, resetting state machine\n", ((DallasIDPtr)bEEPROM)->deviceSubType );
								stateMachine = kSTATE_RESET_READ_MEMORY;			//	bad data then retry at copy of EEPROM to scratchpad
							}
							break;
						default:
							index++;
							if ( index >= size ) { stateMachine = kSTATE_COMPLETED; }
							break;
					}
				}
				break;
			case kSTATE_COMPLETED:
				break;
		}
		if ( retryCount && ( kSTATE_RESET_READ_MEMORY == stateMachine ) ) {
			IOSleep ( 1 );
		}
	}
	if ( kSTATE_COMPLETED == stateMachine ) { resultSuccess = TRUE; }
	
exit:
	debug5IOLog ( "- AppleDallasDriver::readDataROM ( %X, %X, %X ) returns %X\n", (unsigned int)bEEPROM, (unsigned int)dallasAddress, (unsigned int)size, (unsigned int)resultSuccess );
    debug2IOLog("[DALLAS] readDataROM returns %dx\n", (unsigned int)resultSuccess);
    return resultSuccess;
}

//================================================================================
bool AppleDallasDriver::readSerialNumberROM (UInt8 *bROM)
{
    IOMemoryMap     *map = NULL;
    UInt8           *gpioPtr = NULL;
    int              index;
    bool             failure;
    bool             resultSuccess;
	int				retryCount;
    
    resultSuccess = FALSE;
    map = gpioRegMem->map (0);
    FailIf (!map, exit);
    gpioPtr = (UInt8*)map->getVirtualAddress ();
    debug2IOLog("[DALLAS] GPIO16 Register Value = 0x%02x\n", *gpioPtr);
    FailIf (!gpioPtr, exit);
    
    // Look for ROM present
    FailWithAction(ROMReset(gpioPtr), debugIOLog("[DALLAS] No speaker ROM detected\n"), exit);
    
    // Read 64b ROM
    failure = TRUE;
    debugIOLog("[DALLAS] Reading 64b ROM\n");
	retryCount = kRetryCountSeed;
    while (failure && retryCount) {
        while (failure && retryCount) {
            ROMReset(gpioPtr);
            failure = ROMSendByte( gpioPtr, kROMReadROM, 0 );
			retryCount--;
        }

        for ( index = 0; index < 8 && !failure; index++ ) {
            failure |= ROMReadByte ( gpioPtr, &bROM[index] );
		}
    }
    if ( !failure ) {
		resultSuccess = TRUE;
	}
exit:
    return resultSuccess;
}

//================================================================================
bool AppleDallasDriver::start(IOService *provider)
{
    IORegistryEntry *gpio;
    IORegistryEntry *dallasGPIO;
    OSData          *tmpData  = NULL;
    UInt32          *gpioAddr = NULL;
    UInt8            bROM[8];
    UInt8            bEEPROM[32];
    UInt8            bAppReg[8];
    bool             result;
    int				 index;

    debugIOLog ("AppleDallasDriver Starting\n");
    
    result = super::start(provider);
	FailIf (FALSE == result, exit);
	result = FALSE;

    gpio = provider->getParentEntry (gIODTPlane);
    FailIf (!gpio, exit);
        
	dallasGPIO = FindEntryByProperty (gpio, "AAPL,driver-name", ".DallasDriver");
    FailIf (!dallasGPIO, exit);

    // get the hard coded memory address
    tmpData = OSDynamicCast (OSData, dallasGPIO->getProperty ("AAPL,address"));
    FailIf (!tmpData, exit);
    gpioAddr = (UInt32*)tmpData->getBytesNoCopy ();
    FailIf (!gpioAddr, exit);

    //  and convert it a virtual address
    gpioRegMem = IODeviceMemory::withRange (*gpioAddr, sizeof (UInt8));
    FailIf (!gpioRegMem, exit);

	for (index = 0; index < 8; index++)
		bROM[index] = 0;
		
	for (index = 0; index < 32; index++)
		bEEPROM[index] = 0;

	for (index = 0; index < 8; index++)
		bAppReg[index] = 0;

   // (void)readROM (bROM, bEEPROM, bAppReg);
   // ROMCheckCRC (bROM);

	registerService ();
	result = TRUE;

exit:
    return result;
}

//================================================================================
void AppleDallasDriver::stop (IOService *provider)
{
    debugIOLog ("AppleDallasDriver Stopping\n");
    super::stop (provider);
}

//================================================================================
//	The parameters are of the form:
//		UInt8				bROM[8];
//		UInt8				bEEPROM[32];  <--- use this for speaker id
//		UInt8				bAppReg[8];
//	NOTE:	There is no CRC within the EEPROM.  Only the lasered ROM has CRC!!!
//			This function only accesses the EEPROM.
bool AppleDallasDriver::getSpeakerID (UInt8 *bEEPROM)
{
	bool				resultSuccess;
    IOMemoryMap *		map = NULL;
    UInt8 *				gpioPtr = NULL;
	UInt8				savedGPIO;
	
	debug2IOLog ( "+ AppleDallasDriver::getSpeakerID ( %X )\n", (unsigned int)bEEPROM );

	resultSuccess = FALSE;
	
    map = gpioRegMem->map (0);
    FailIf (!map, exit);
    gpioPtr = (UInt8*)map->getVirtualAddress ();

    debug2IOLog("... GPIO16 Register Value = 0x%02x\n", *gpioPtr);
    FailIf (!gpioPtr, exit);

	savedGPIO = *gpioPtr;

	if ( NULL != bEEPROM ) {
		debug4IOLog ( "... About to readDataROM ( %X, %X, %X )\n", (unsigned int)bEEPROM, (unsigned int)kDallasIDAddress, (unsigned int)sizeof ( SpkrID ) );
 		resultSuccess = readDataROM (bEEPROM, kDallasIDAddress, sizeof ( SpkrID ) );
	}

	*gpioPtr = savedGPIO;
	OSSynchronizeIO();
	
exit:
	debug3IOLog ( "- AppleDallasDriver::getSpeakerID ( %X ) returns %X\n", (unsigned int)bEEPROM, (unsigned int)resultSuccess );

	return resultSuccess;
}
