/*
 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (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.
 * 
 * This 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@
 */
/*
 * Copyright (c) 1999 Apple Computer, Inc.  All rights reserved.
 *
 *  DRI: Josh de Cesare
 *
 */

#include <ppc/proc_reg.h>

#include <IOKit/IOLib.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/IODeviceMemory.h>
#include <IOKit/IOPlatformExpert.h>
#include <IOKit/ppc/IODBDMA.h>

#include "USBKeyLargo.h"
#include "KeyLargo.h"

#define RevertEndianness32(X) ((X & 0x000000FF) << 24) | \
((X & 0x0000FF00) << 16) | \
((X & 0x00FF0000) >> 16) | \
((X & 0xFF000000) >> 24)

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#define super AppleMacIO

OSDefineMetaClassAndStructors(KeyLargo, AppleMacIO);

KeyLargo *gHostKeyLargo = NULL;

bool KeyLargo::init(OSDictionary * properties)
{
  // Just to be sure we are not going to use the
  // backup structures by mistake let's invalidate
  // their contents.
  savedKeyLargState.thisStateIsValid = false;
  
  // And by default the wireless slot is powered on:
  cardStatus.cardPower = true;
  
  // sets the usb pointers to null:
  int i;
  for (i = 0; i < kNumUSB; i++)
    usbBus[i] = NULL;
  
  return super::init(properties);
}

bool KeyLargo::start(IOService *provider)
{
  IORegistryEntry *viaPMU;
  OSData          *tmpData;
  UInt32          pmuVersion;

    // Get the bus speed from the Device Tree.
  IORegistryEntry *entry;
  
  entry = fromPath("/", gIODTPlane);
  tmpData = OSDynamicCast(OSData, entry->getProperty("clock-frequency"));
  if (tmpData == 0) return false;
  busSpeed = *(unsigned long *)tmpData->getBytesNoCopy();
  
  // if this is mac-io (as opposed to ext-mac-io) save a reference to it
  tmpData = OSDynamicCast(OSData, provider->getProperty("name"));
  if (tmpData == 0) return false;
  
  if (tmpData->isEqualTo ("mac-io", strlen ("mac-io")))
	gHostKeyLargo = this;
	
  // callPlatformFunction symbols
  keyLargo_resetUniNEthernetPhy = OSSymbol::withCString("keyLargo_resetUniNEthernetPhy");
  keyLargo_restoreRegisterState = OSSymbol::withCString("keyLargo_restoreRegisterState");
  keyLargo_syncTimeBase = OSSymbol::withCString("keyLargo_syncTimeBase");
  keyLargo_saveRegisterState = OSSymbol::withCString("keyLargo_saveRegisterState");
  keyLargo_turnOffIO = OSSymbol::withCString("keyLargo_turnOffIO");
  keyLargo_writeRegUInt8 = OSSymbol::withCString("keyLargo_writeRegUInt8");
  keyLargo_safeWriteRegUInt8 = OSSymbol::withCString("keyLargo_safeWriteRegUInt8");
  keyLargo_safeReadRegUInt8 = OSSymbol::withCString("keyLargo_safeReadRegUInt8");
  keyLargo_safeWriteRegUInt32 = OSSymbol::withCString("keyLargo_safeWriteRegUInt32");
  keyLargo_safeReadRegUInt32 = OSSymbol::withCString("keyLargo_safeReadRegUInt32");
  keyLargo_powerMediaBay = OSSymbol::withCString("powerMediaBay");
  keyLargo_getHostKeyLargo = OSSymbol::withCString("keyLargo_getHostKeyLargo");
  
  // Call MacIO's start.
  if (!super::start(provider))
    return false;

  // sets up the mutex lock:
  mutex = IOSimpleLockAlloc();

  if (mutex != NULL)
     IOSimpleLockInit( mutex );

  // get the base address of KeyLargo.
  keyLargoBaseAddress = fMemory->getVirtualAddress();

  tmpData = OSDynamicCast(OSData, provider->getProperty("device-id"));
  if (tmpData == 0) return false;
  keyLargoDeviceId = *(long *)tmpData->getBytesNoCopy();
  
  tmpData = OSDynamicCast(OSData, provider->getProperty("revision-id"));
  if (tmpData == 0) return false;
  keyLargoVersion = *(long *)tmpData->getBytesNoCopy();
  
  enableCells();


  // Make nubs for the children.
  publishBelow(provider);



  // Get the PMU version to determine support for the WatchDog timer.
  pmuVersion = 0;
  viaPMU = provider->childFromPath("via-pmu", gIODTPlane);
  if (viaPMU != 0) {
    tmpData = OSDynamicCast(OSData, viaPMU->getProperty("pmu-version"));
    if (tmpData != 0) {
      pmuVersion = *(UInt32 *)tmpData->getBytesNoCopy();
      
      if (((pmuVersion & 0x000000FF) == 0x0C) &&
	  (((pmuVersion >> 8) & 0x0000FFFF) >= 0x0000D044)) {
	watchDogTimer = KeyLargoWatchDogTimer::withKeyLargo(this);
      }
    }
  }
  
  // at power on the media bay is on:
  mediaIsOn = true;
  
  registerService();
  
  
  // initialize for Power Management
  initForPM(provider);
  
  // creates the USBPower handlers:
  int i;
  for (i = 0; i < kNumUSB; i++) {
    usbBus[i] = new USBKeyLargo;
    
    if (usbBus[i] != NULL) {
      if ( usbBus[i]->init() && usbBus[i]->attach(this)) {
	usbBus[i]->initForBus(i);                 
      }
      else {
	usbBus[i]->release();
      }  
    }
  }

  
  return true;
}

void KeyLargo::stop(IOService *provider)
{
  // releases the USBPower handlers:
  int i;
  for (i = 0; i < kNumUSB; i++) {
    if (usbBus[i] != NULL) {
      usbBus[i]->release();
    }
  }

  if (mutex != NULL)
    IOSimpleLockFree( mutex );
}

long long KeyLargo::syncTimeBase(void)
{
  unsigned long   cnt;
  unsigned long   gtLow, gtHigh, gtHigh2;
  unsigned long   tbLow, tbHigh, tbHigh2;
  long long       tmp, diffTicks, ratioLow, ratioHigh;
  
  ratioLow = (busSpeed << 32) / (kKeyLargoGTimerFreq * 4);
  ratioHigh = ratioLow >> 32;
  ratioLow &= 0xFFFFFFFFULL;
  
  // Save the old time base.
  do {
    tbHigh  = mftbu();
    tbLow   = mftb();
    tbHigh2 = mftbu();
  } while (tbHigh != tbHigh2);
  diffTicks = ((long long)tbHigh << 32) | tbLow;
  
  // Do the sync twice to make sure it is cached.
  for (cnt = 0; cnt < 2; cnt++) {
    // Read the Global Counter.
    do {
      gtHigh  = readRegUInt32(kKeyLargoCounterHiOffset);
      gtLow   = readRegUInt32(kKeyLargoCounterLoOffset);
      gtHigh2 = readRegUInt32(kKeyLargoCounterHiOffset);
    } while (gtHigh != gtHigh2);
    
    tmp = gtHigh * ratioLow + gtLow * ratioHigh +
      ((gtLow * ratioLow + 0x080000000ULL) >> 32);
    tbHigh = gtHigh * ratioHigh + (tmp >> 32);
    tbLow = tmp & 0xFFFFFFFFULL;
    
    mttb(0);
    mttbu(tbHigh);
    mttb(tbLow);
  }
  
  diffTicks = (((long long)tbHigh << 32) | tbLow) - diffTicks;
  
  return diffTicks;
}

void KeyLargo::turnOffKeyLargoIO(bool restart)
{
    UInt32 regTemp;

    if (!restart) {
        kprintf("KeyLargo::turnOffIO( --FALSE-- )\n");
    }

    if (!restart) {
        // turning off the USB clocks:
        regTemp = readRegUInt32(kKeyLargoFCR0);
        regTemp |= kKeyLargoFCR0SleepBitsSet;
        writeRegUInt32(kKeyLargoFCR0, regTemp);
        IODelay(1000);
    }

    regTemp = readRegUInt32(kKeyLargoFCR0);
    regTemp |= kKeyLargoFCR0SleepBitsSet;
    regTemp &= ~kKeyLargoFCR0SleepBitsClear;
    writeRegUInt32(kKeyLargoFCR0, regTemp);

    // Set MediaBay Dev1 Enable before IDE Resets.
    regTemp = readRegUInt32(kKeyLargoMediaBay);
    regTemp |= kKeyLargoMB0DevEnable;
    writeRegUInt32(kKeyLargoMediaBay, regTemp);

    regTemp = readRegUInt32(kKeyLargoFCR1);
    regTemp |= kKeyLargoFCR1SleepBitsSet;
    regTemp &= ~kKeyLargoFCR1SleepBitsClear;
    writeRegUInt32(kKeyLargoFCR1, regTemp);

    regTemp = readRegUInt32(kKeyLargoFCR2);
    regTemp |= kKeyLargoFCR2SleepBitsSet;
    regTemp &= ~kKeyLargoFCR2SleepBitsClear;
    writeRegUInt32(kKeyLargoFCR2, regTemp);

    regTemp = readRegUInt32(kKeyLargoFCR3);
    if (keyLargoVersion >= kKeyLargoVersion2) {
        regTemp |= kKeyLargoFCR3ShutdownPLL2X;
        if (!restart) {
            regTemp |= kKeyLargoFCR3ShutdownPLLTotal;
        }
    }
    if (restart) {
        regTemp |= kKeyLargoFCR3RestartBitsSet;
        regTemp &= ~kKeyLargoFCR3RestartBitsClear;
    } else {
        regTemp |= kKeyLargoFCR3SleepBitsSet;
        regTemp &= ~kKeyLargoFCR3SleepBitsClear;
    }
    writeRegUInt32(kKeyLargoFCR3, regTemp);

    if (restart) {
        // turning on the USB clocks
        regTemp = readRegUInt32(kKeyLargoFCR0);
        regTemp &= ~kKeyLargoFCR0USBRefSuspend;
        writeRegUInt32(kKeyLargoFCR0, regTemp);
        IODelay(1000);

        // enables the keylargo cells we are going to need:
        enableCells();
    }
    
    if (restart) {
        kprintf("KeyLargo::turnOffIO( --TRUE-- )\n");
    }
}

void KeyLargo::turnOffPangeaIO(bool restart)
{
    UInt32 regTemp;

    // FCR0
    regTemp = readRegUInt32(kFCR0Offset);
    regTemp |= kPangeaFCR0SleepBitsSet;
    regTemp &= ~kPangeaFCR0SleepBitsClear;
    writeRegUInt32(kFCR0Offset, regTemp);

    // FCR1
    regTemp = readRegUInt32(kFCR1Offset);
    regTemp |= kPangeaFCR1SleepBitsSet;
//regTemp |= kFCR1_UltraIDE_Reset_l;   
    regTemp &= ~kPangeaFCR1SleepBitsClear;
    writeRegUInt32(kFCR1Offset, regTemp);

    // FCR2
    regTemp = readRegUInt32(kFCR2Offset);
    regTemp |= kPangeaFCR2SleepBitsSet;
    regTemp &= ~kPangeaFCR2SleepBitsClear;
    writeRegUInt32(kFCR2Offset, regTemp);

    // FCR3
    regTemp = readRegUInt32(kFCR3Offset);
    regTemp |= kPangeaFCR3SleepBitsSet;
    regTemp &= ~kPangeaFCR3SleepBitsClear;
    writeRegUInt32(kFCR3Offset, regTemp);

    // FCR4
    regTemp = readRegUInt32(kFCR4Offset);
    regTemp |= kPangeaFCR4SleepBitsSet;
    regTemp &= ~kPangeaFCR4SleepBitsClear;
    writeRegUInt32(kFCR4Offset, regTemp);
}

// Uncomment the following define if you need to see the state of
// the media bay register:
//#define LOG_MEDIA_BAY_TRANSACTIONS

void KeyLargo::powerMediaBay(bool powerOn, UInt8 powerDevice)
{
    UInt32 regTemp;
    UInt32 whichDevice = powerDevice;
    
    IOLog("KeyLargo::powerMediaBay(%s) 0x%02x\n", (powerOn ? "TRUE" : "FALSE"), powerDevice);

    if (mediaIsOn == powerOn) {
#ifdef LOG_MEDIA_BAY_TRANSACTIONS
        IOLog("KeyLargo::powerMediaBay mbreg = 0x%08lx\n",readRegUInt32(kKeyLargoMediaBay));
#endif
        return;
    }
    
    // Makes sure that the reset bit is off:
    regTemp = readRegUInt32(kKeyLargoMediaBay);

#ifdef LOG_MEDIA_BAY_TRANSACTIONS
    IOLog(" 0 KeyLargo::powerMediaBay = 0x%08lx\n",regTemp);
#endif

    regTemp &= (~(kKeyLargoMB0DevReset));

#ifdef LOG_MEDIA_BAY_TRANSACTIONS
    IOLog(" 1 KeyLargo::powerMediaBay = 0x%08lx\n",regTemp);
#endif

    writeRegUInt32(kKeyLargoMediaBay, regTemp);

    if (powerOn) {
        // we are powering on the bay and need a delay between turning on
        // media bay power and enabling the bus
        regTemp = readRegUInt32(kKeyLargoMediaBay);

#ifdef LOG_MEDIA_BAY_TRANSACTIONS
        IOLog(" 2 KeyLargo::powerMediaBay = 0x%08lx\n",regTemp);
#endif

        regTemp &= (~(kKeyLargoMB0DevPower));

#ifdef LOG_MEDIA_BAY_TRANSACTIONS
        IOLog(" 3 KeyLargo::powerMediaBay = 0x%08lx\n",regTemp);
#endif
        writeRegUInt32(kKeyLargoMediaBay, regTemp);
	IODelay(500);
    }

    // to turn on the buses, we ensure all buses are off and then turn on the requested bus
    regTemp = readRegUInt32(kKeyLargoMediaBay);

#ifdef LOG_MEDIA_BAY_TRANSACTIONS
    IOLog(" 4 KeyLargo::powerMediaBay = 0x%08lx\n",regTemp);
#endif

    regTemp &= (~(kKeyLargoMB0DevEnable));

#ifdef LOG_MEDIA_BAY_TRANSACTIONS
    IOLog(" 5 KeyLargo::powerMediaBay = 0x%08lx\n",regTemp);
#endif
    writeRegUInt32(kKeyLargoMediaBay, regTemp);
    IODelay(500);

    // and turns on the right bus:
    regTemp = readRegUInt32(kKeyLargoMediaBay);

#ifdef LOG_MEDIA_BAY_TRANSACTIONS
    IOLog(" 6 KeyLargo::powerMediaBay = 0x%08lx\n",regTemp);
#endif
    regTemp |= (whichDevice << 11);

#ifdef LOG_MEDIA_BAY_TRANSACTIONS
    IOLog(" 7 KeyLargo::powerMediaBay = 0x%08lx\n",regTemp);
#endif

    writeRegUInt32(kKeyLargoMediaBay, regTemp);
    IODelay(500);

    if (!powerOn) {
        // turn off media bay power:
        regTemp = readRegUInt32(kKeyLargoMediaBay);

#ifdef LOG_MEDIA_BAY_TRANSACTIONS
        IOLog(" 8 KeyLargo::powerMediaBay = 0x%08lx\n",regTemp);
#endif
        regTemp |= kKeyLargoMB0DevPower;

#ifdef LOG_MEDIA_BAY_TRANSACTIONS
        IOLog(" 9 KeyLargo::powerMediaBay = 0x%08lx\n",regTemp);
#endif
        writeRegUInt32(kKeyLargoMediaBay, regTemp);
    }
    else {
        // take us out of reset:
        regTemp = readRegUInt32(kKeyLargoMediaBay);

#ifdef LOG_MEDIA_BAY_TRANSACTIONS
        IOLog(" 10 KeyLargo::powerMediaBay = 0x%08lx\n",regTemp);
#endif
        regTemp |= kKeyLargoMB0DevReset;

#ifdef LOG_MEDIA_BAY_TRANSACTIONS
        IOLog(" 11 KeyLargo::powerMediaBay = 0x%08lx\n",regTemp);
#endif
        writeRegUInt32(kKeyLargoMediaBay, regTemp);

        IODelay(500);

        // And also takes the ATA bus out of reset:
        regTemp = readRegUInt32(kKeyLargoFCR1);
        regTemp |= kKeyLargoFCR1EIDE0Reset;
        writeRegUInt32(kKeyLargoFCR1, regTemp);
    }
    IODelay(500);
    
    mediaIsOn = powerOn;
}

void KeyLargo::powerWireless(bool powerOn)
{
    // if we are already in the wanted power
    // state just exit:
    if (cardStatus.cardPower == powerOn)
        return;

    if (powerOn) {
        // power the card on by setting the registers with their
        // back-up copy:
        writeRegUInt8(kKeyLargoExtIntGPIOBase + 10, cardStatus.wirelessCardReg[0]);
        writeRegUInt8(kKeyLargoExtIntGPIOBase + 13, cardStatus.wirelessCardReg[1]);
        writeRegUInt8(kKeyLargoGPIOBase + 13, cardStatus.wirelessCardReg[2]);
        writeRegUInt8(kKeyLargoGPIOBase + 14, cardStatus.wirelessCardReg[3]);
        writeRegUInt8(kKeyLargoGPIOBase + 15, cardStatus.wirelessCardReg[4]);
    }
    else {
        // makes a copy of all the wireless slot register and
        // clears the registers:
        cardStatus.wirelessCardReg[0] = readRegUInt8(kKeyLargoExtIntGPIOBase + 10);
        cardStatus.wirelessCardReg[1] = readRegUInt8(kKeyLargoExtIntGPIOBase + 13);
        cardStatus.wirelessCardReg[2] = readRegUInt8(kKeyLargoGPIOBase + 13);
        cardStatus.wirelessCardReg[3] = readRegUInt8(kKeyLargoGPIOBase + 14);
        cardStatus.wirelessCardReg[4] = readRegUInt8(kKeyLargoGPIOBase + 15);

        writeRegUInt8(kKeyLargoExtIntGPIOBase + 10, 0);
        writeRegUInt8(kKeyLargoExtIntGPIOBase + 13, 0);
        writeRegUInt8(kKeyLargoGPIOBase + 13, 0);
        writeRegUInt8(kKeyLargoGPIOBase + 14, 0);
        writeRegUInt8(kKeyLargoGPIOBase + 15, 0);
    }

    // and updates the status:
    cardStatus.cardPower = powerOn;
}

void KeyLargo::enableCells()
{
    UInt32 *fcr0Address = (UInt32*)(keyLargoBaseAddress + 0x38); // offset on the scc register KFCR00
    UInt32 *fcr2Address = (UInt32*)(keyLargoBaseAddress + 0x40); // offset on the modem register KFCR20
    unsigned int debugFlags;
    UInt32 bitMask;
    UInt32 oldValue;

    if (!PE_parse_boot_arg("debug", &debugFlags))
        debugFlags = 0;

    if( debugFlags & 0x18) {
        // Enables the SCC cell:
        bitMask = (1 << 6);     // Enables SCC
        //bitMask |= (1 << 5);  // Enables SCC B (which I do not belive is in use i any core99)
        bitMask |= (1 << 4);    // Enables SCC A
        bitMask |= (1 << 1);    // Enables SCC Interface A
        //bitMask |= (1 << 0);  // Enables SCC Interface B (which I do not belive is in use i any core99)

        oldValue = *fcr0Address;
        eieio();

        oldValue |= RevertEndianness32(bitMask);

        *fcr0Address = oldValue;
        eieio();
    }

    // Enables the mpic cell:
    bitMask = (1 << 17);     // Enables MPIC

    oldValue = *fcr2Address;
    eieio();

    oldValue |= RevertEndianness32(bitMask);

    *fcr2Address = oldValue;
    eieio();
}

// NOTE: Marco changed the save and restore state to save all keylargo registers.
// this is a temporary fix, the real code should save and restore all registers
// in each specific driver (VIA, MPIC ...) However for now it is easier to follow
// the MacOS9 policy to do everything here.
void KeyLargo::saveRegisterState(void)
{
    saveKeyLargoState();
    saveVIAState();
    powerWireless(false);
    savedKeyLargState.thisStateIsValid = true;
}

void KeyLargo::restoreRegisterState(void)
{
    if (savedKeyLargState.thisStateIsValid) {
        restoreKeyLargoState();
        restoreVIAState();
        powerWireless(true);
    }

    savedKeyLargState.thisStateIsValid = false;
}

UInt8 KeyLargo::readRegUInt8(unsigned long offset)
{
    return *(UInt8 *)(keyLargoBaseAddress + offset);
}

void KeyLargo::writeRegUInt8(unsigned long offset, UInt8 data)
{
    *(UInt8 *)(keyLargoBaseAddress + offset) = data;

    eieio();
}

void KeyLargo::safeWriteRegUInt8(unsigned long offset, UInt8 mask, UInt8 data)
{
  IOInterruptState intState;

  if ( mutex  != NULL )
     intState = IOSimpleLockLockDisableInterrupt(mutex);

  UInt8 currentReg = readRegUInt8(offset);
  currentReg = (currentReg & ~mask) | (data & mask);
  writeRegUInt8(offset, currentReg);
  
  if ( mutex  != NULL )
     IOSimpleLockUnlockEnableInterrupt(mutex, intState);
}

UInt8 KeyLargo::safeReadRegUInt8(unsigned long offset)
{
  IOInterruptState intState;

  if ( mutex  != NULL )
     intState = IOSimpleLockLockDisableInterrupt(mutex);
  
  UInt8 currentReg = readRegUInt8(offset);
  
  if ( mutex  != NULL )
     IOSimpleLockUnlockEnableInterrupt(mutex, intState);

  return (currentReg);  
}

UInt32 KeyLargo::readRegUInt32(unsigned long offset)
{
    return lwbrx(keyLargoBaseAddress + offset);
}

void KeyLargo::writeRegUInt32(unsigned long offset, UInt32 data)
{
  stwbrx(data, keyLargoBaseAddress + offset);
  eieio();
}

void KeyLargo::safeWriteRegUInt32(unsigned long offset, UInt32 mask, UInt32 data)
{
  IOInterruptState intState;

  if ( mutex  != NULL )
     intState = IOSimpleLockLockDisableInterrupt(mutex);

  UInt32 currentReg = readRegUInt32(offset);
  currentReg = (currentReg & ~mask) | (data & mask);
  writeRegUInt32(offset, currentReg);
  
  if ( mutex  != NULL )
     IOSimpleLockUnlockEnableInterrupt(mutex, intState);
}

UInt32 KeyLargo::safeReadRegUInt32(unsigned long offset)
{
  IOInterruptState intState;

  if ( mutex  != NULL )
     intState = IOSimpleLockLockDisableInterrupt(mutex);
  
  UInt32 currentReg = readRegUInt32(offset);
  
  if ( mutex  != NULL )
     IOSimpleLockUnlockEnableInterrupt(mutex, intState);

  return (currentReg);  
}

// --------------------------------------------------------------------------
// Method: initForPM
//
// Purpose:
//   initialize the driver for power managment and register ourselves with
//   superclass policy-maker
void KeyLargo::initForPM (IOService *provider)
{
    PMinit();                   // initialize superclass variables
    provider->joinPMtree(this); // attach into the power management hierarchy  

    // KeyLargo has only 2 power states::
    // 0 OFF
    // 1 all ON
    // Pwer state fields:
    // unsigned long	version;		// version number of this struct
    // IOPMPowerFlags	capabilityFlags;	// bits that describe (to interested drivers) the capability of the device in this state
    // IOPMPowerFlags	outputPowerCharacter;	// description (to power domain children) of the power provided in this state
    // IOPMPowerFlags	inputPowerRequirement;	// description (to power domain parent) of input power required in this state
    // unsigned long	staticPower;		// average consumption in milliwatts
    // unsigned long	unbudgetedPower;	// additional consumption from separate power supply (mw)
    // unsigned long	powerToAttain;		// additional power to attain this state from next lower state (in mw)
    // unsigned long	timeToAttain;		// time required to enter this state from next lower state (in microseconds)
    // unsigned long	settleUpTime;		// settle time required after entering this state from next lower state (microseconds)
    // unsigned long	timeToLower;		// time required to enter next lower state from this one (in microseconds)
    // unsigned long	settleDownTime;		// settle time required after entering next lower state from this state (microseconds)
    // unsigned long	powerDomainBudget;	// power in mw a domain in this state can deliver to its children

    // NOTE: all these values are made up since now I do not have areal clue of what to put.
#define kNumberOfPowerStates 3

    static IOPMPowerState ourPowerStates[kNumberOfPowerStates] = {
    { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 1, 0, IOPMSoftSleep, IOPMSoftSleep, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 1, IOPMDeviceUsable, IOPMPowerOn, IOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0 }
};


    // register ourselves with ourself as policy-maker
    if (pm_vars != NULL)
        registerPowerDriver(this, ourPowerStates, kNumberOfPowerStates);
}

// Method: setPowerState
//
// Purpose:
IOReturn KeyLargo::setPowerState(unsigned long powerStateOrdinal, IOService* whatDevice)
{
    // Do not do anything if the state is inavalid.
    if (powerStateOrdinal >= kNumberOfPowerStates)
        return IOPMAckImplied;

    if ( powerStateOrdinal == 0 ) {
        kprintf("KeyLargo would be powered off here\n");
    }
    if ( powerStateOrdinal == 1 ) {
        kprintf("KeyLargo would be powered on here\n");
    }
    return IOPMAckImplied;
}

#define MPIC_OFFSET 0x40000
#define VIA_OFFSET 0x16000

// Method: saveKeyLargoState
//
// Purpose:
//        saves the state of all the meaningful registers into a local buffer.
//    this method is almost a copy and paste of the orignal MacOS9 function
//    SaveKeyLargoState. The code does not care bout the endiannes of the
//    registers since the values are not meaningful, we just wish to save them
//    and restore them.
void KeyLargo::saveKeyLargoState(void)
{
    KeyLargoMPICState*		savedKeyLargoMPICState;
    KeyLargoGPIOState*		savedKeyLargoGPIOState;
    KeyLargoConfigRegistersState*	savedKeyLargoConfigRegistersState;
    KeyLargoDBDMAState*		savedKeyLargoDBDMAState;
    KeyLargoAudioState*		savedKeyLargoAudioState;
    KeyLargoI2SState*		savedKeyLargoI2SState;
    UInt32				channelOffset;
    int				i;

    // base of the keylargo regiters.
    UInt8* keyLargoBaseAddr = (UInt8*)keyLargoBaseAddress;
    UInt8* mpicBaseAddr = (UInt8*)keyLargoBaseAddress + MPIC_OFFSET;

    // Save GPIO portion of KeyLargo.

    savedKeyLargoGPIOState = &savedKeyLargState.savedGPIOState;

    savedKeyLargoGPIOState->gpioLevels[0] = *(UInt32 *)(keyLargoBaseAddr + 0x50);
    savedKeyLargoGPIOState->gpioLevels[1] = *(UInt32 *)(keyLargoBaseAddr + 0x54);

    for (i = 0; i < 18; i++)
    {
        savedKeyLargoGPIOState->extIntGPIO[i] = *(UInt8 *)(keyLargoBaseAddr + 0x58 + i);
    }

    for (i = 0; i < 17; i++)
    {
        savedKeyLargoGPIOState->gpio[i] = *(UInt8 *)(keyLargoBaseAddr + 0x6A + i);
    }

    // Save Audio registers.

    savedKeyLargoAudioState = &savedKeyLargState.savedAudioState;

    for (i = 0, channelOffset = 0; i < 25; i++, channelOffset += 0x0010)
    {
        savedKeyLargoAudioState->audio[i] = *(UInt32 *) (keyLargoBaseAddr + 0x14000 + channelOffset);
    }

    // Save I2S registers.

    savedKeyLargoI2SState = &savedKeyLargState.savedI2SState;

    for (i = 0, channelOffset = 0; i < 10; i++, channelOffset += 0x0010)
    {
        savedKeyLargoI2SState->i2s[i] = *(UInt32 *) (keyLargoBaseAddr + 0x10000 + channelOffset);
        savedKeyLargoI2SState->i2s[i + 1] = *(UInt32 *) (keyLargoBaseAddr + 0x11000 + channelOffset);
    }

    // Save DBDMA registers.  There are thirteen channels on KeyLargo.

    savedKeyLargoDBDMAState = &savedKeyLargState.savedDBDMAState;

    for (i = 0, channelOffset = 0; i < 13; i++, channelOffset += 0x0100)
    {
        volatile DBDMAChannelRegisters*				currentChannel;

        currentChannel = (volatile DBDMAChannelRegisters *) (keyLargoBaseAddr + 0x8000 + channelOffset);

        savedKeyLargoDBDMAState->dmaChannel[i].commandPtrLo = IOGetDBDMACommandPtr(currentChannel);
        savedKeyLargoDBDMAState->dmaChannel[i].interruptSelect = IOGetDBDMAInterruptSelect(currentChannel);
        savedKeyLargoDBDMAState->dmaChannel[i].branchSelect = IOGetDBDMABranchSelect(currentChannel);
        savedKeyLargoDBDMAState->dmaChannel[i].waitSelect = IOGetDBDMAWaitSelect(currentChannel);
    }

    // Save configuration registers in KeyLargo (MediaBay Configuration Register, FCR 0-4)

    savedKeyLargoConfigRegistersState = &savedKeyLargState.savedConfigRegistersState;

    savedKeyLargoConfigRegistersState->mediaBay = *(UInt32 *)(keyLargoBaseAddr + kMediaBayRegOffset);

    for (i = 0; i < 5; i++)
    {
        savedKeyLargoConfigRegistersState->featureControl[i] = ((UInt32 *)(keyLargoBaseAddr + kFCR0Offset))[i];
    }

    // Save MPIC portion of KeyLargo.

    savedKeyLargoMPICState = &savedKeyLargState.savedMPICState;

    savedKeyLargoMPICState->mpicIPI[0] = *(UInt32 *)(mpicBaseAddr + MPICIPI0);
    savedKeyLargoMPICState->mpicIPI[1] = *(UInt32 *)(mpicBaseAddr + MPICIPI1);
    savedKeyLargoMPICState->mpicIPI[2] = *(UInt32 *)(mpicBaseAddr + MPICIPI2);
    savedKeyLargoMPICState->mpicIPI[3] = *(UInt32 *)(mpicBaseAddr + MPICIPI3);

    savedKeyLargoMPICState->mpicSpuriousVector = *(UInt32 *)(mpicBaseAddr + MPICSpuriousVector);
    savedKeyLargoMPICState->mpicTimerFrequencyReporting = *(UInt32 *)(mpicBaseAddr + MPICTimeFreq);

    savedKeyLargoMPICState->mpicTimers[0] = *(MPICTimers *)(mpicBaseAddr + MPICTimerBase0);
    savedKeyLargoMPICState->mpicTimers[1] = *(MPICTimers *)(mpicBaseAddr + MPICTimerBase1);
    savedKeyLargoMPICState->mpicTimers[2] = *(MPICTimers *)(mpicBaseAddr + MPICTimerBase2);
    savedKeyLargoMPICState->mpicTimers[3] = *(MPICTimers *)(mpicBaseAddr + MPICTimerBase3);

    for (i = 0; i < 64; i++)
    {
        // Make sure that the "active" bit is cleared.
        savedKeyLargoMPICState->mpicInterruptSourceVectorPriority[i] = *(UInt32 *)(mpicBaseAddr + MPICIntSrcVectPriBase + i * MPICIntSrcSize) & (~0x00000040);
        savedKeyLargoMPICState->mpicInterruptSourceDestination[i] = *(UInt32 *)(mpicBaseAddr + MPICIntSrcDestBase + i * MPICIntSrcSize);
    }

    savedKeyLargoMPICState->mpicCurrentTaskPriorities[0] = *(UInt32 *)(mpicBaseAddr + MPICP0CurrTaskPriority);
    savedKeyLargoMPICState->mpicCurrentTaskPriorities[1] = *(UInt32 *)(mpicBaseAddr + MPICP1CurrTaskPriority);
    savedKeyLargoMPICState->mpicCurrentTaskPriorities[2] = *(UInt32 *)(mpicBaseAddr + MPICP2CurrTaskPriority);
    savedKeyLargoMPICState->mpicCurrentTaskPriorities[3] = *(UInt32 *)(mpicBaseAddr + MPICP3CurrTaskPriority);
}


// Method: restoreKeyLargoState
//
// Purpose:
//        restores the state of all the meaningful registers from a local buffer.
//    this method is almost a copy and paste of the orignal MacOS9 function
//    RestoreKeyLargoState. The code does not care bout the endiannes of the
//    registers since the values are not meaningful, we just wish to save them
//    and restore them.
void KeyLargo::restoreKeyLargoState(void)
{
    KeyLargoMPICState*				savedKeyLargoMPICState;
    KeyLargoGPIOState*				savedKeyLargoGPIOState;
    KeyLargoConfigRegistersState*	savedKeyLargoConfigRegistersState;
    KeyLargoDBDMAState*				savedKeyLargoDBDMAState;
    KeyLargoAudioState*				savedKeyLargoAudioState;
    KeyLargoI2SState*				savedKeyLargoI2SState;
    UInt32						channelOffset;
    int							i;

    // base of the keylargo regiters.
    UInt8* keyLargoBaseAddr = (UInt8*)keyLargoBaseAddress;
    UInt8* mpicBaseAddr = (UInt8*)keyLargoBaseAddress + MPIC_OFFSET;

    // Restore MPIC portion of KeyLargo.


    savedKeyLargoMPICState = &savedKeyLargState.savedMPICState;

    *(UInt32 *)(mpicBaseAddr + MPICIPI0) = savedKeyLargoMPICState->mpicIPI[0];
    eieio();
    *(UInt32 *)(mpicBaseAddr + MPICIPI1) = savedKeyLargoMPICState->mpicIPI[1];
    eieio();
    *(UInt32 *)(mpicBaseAddr + MPICIPI2) = savedKeyLargoMPICState->mpicIPI[2];
    eieio();
    *(UInt32 *)(mpicBaseAddr + MPICIPI3) = savedKeyLargoMPICState->mpicIPI[3];
    eieio();

    *(UInt32 *)(mpicBaseAddr + MPICSpuriousVector) = savedKeyLargoMPICState->mpicSpuriousVector;
    eieio();
    *(UInt32 *)(mpicBaseAddr + MPICTimeFreq) = savedKeyLargoMPICState->mpicTimerFrequencyReporting;
    eieio();

    *(MPICTimers *)(mpicBaseAddr + MPICTimerBase0) = savedKeyLargoMPICState->mpicTimers[0];
    eieio();
    *(MPICTimers *)(mpicBaseAddr + MPICTimerBase1) = savedKeyLargoMPICState->mpicTimers[1];
    eieio();
    *(MPICTimers *)(mpicBaseAddr + MPICTimerBase2) = savedKeyLargoMPICState->mpicTimers[2];
    eieio();
    *(MPICTimers *)(mpicBaseAddr + MPICTimerBase3) = savedKeyLargoMPICState->mpicTimers[3];
    eieio();

    for (i = 0; i < 64; i++)
    {
        *(UInt32 *)(mpicBaseAddr + MPICIntSrcVectPriBase + i * MPICIntSrcSize) = savedKeyLargoMPICState->mpicInterruptSourceVectorPriority[i];
        eieio();
        *(UInt32 *)(mpicBaseAddr + MPICIntSrcDestBase + i * MPICIntSrcSize) = savedKeyLargoMPICState->mpicInterruptSourceDestination[i];
        eieio();
    }

    *(UInt32 *)(mpicBaseAddr + MPICP0CurrTaskPriority) = savedKeyLargoMPICState->mpicCurrentTaskPriorities[0];
    eieio();
    *(UInt32 *)(mpicBaseAddr + MPICP1CurrTaskPriority) = savedKeyLargoMPICState->mpicCurrentTaskPriorities[1];
    eieio();
    *(UInt32 *)(mpicBaseAddr + MPICP2CurrTaskPriority) = savedKeyLargoMPICState->mpicCurrentTaskPriorities[2];
    eieio();
    *(UInt32 *)(mpicBaseAddr + MPICP3CurrTaskPriority) = savedKeyLargoMPICState->mpicCurrentTaskPriorities[3];
    eieio();


    // Restore configuration registers in KeyLargo (MediaBay Configuration Register, FCR 0-4)

    savedKeyLargoConfigRegistersState = &savedKeyLargState.savedConfigRegistersState;

    *(UInt32 *)(keyLargoBaseAddr + kMediaBayRegOffset) = savedKeyLargoConfigRegistersState->mediaBay;
    eieio();


    for (i = 0; i < 5; i++)
    {
        ((UInt32 *)(keyLargoBaseAddr + kFCR0Offset))[i] = savedKeyLargoConfigRegistersState->featureControl[i];
        eieio();
    }

    IODelay(250);

    // Restore DBDMA registers.  There are thirteen channels on KeyLargo.

    savedKeyLargoDBDMAState = &savedKeyLargState.savedDBDMAState;

    for (i = 0, channelOffset = 0; i < 13; i++, channelOffset += 0x0100)
    {
        volatile DBDMAChannelRegisters*				currentChannel;

        currentChannel = (volatile DBDMAChannelRegisters *) (keyLargoBaseAddr + 0x8000 + channelOffset);

        IODBDMAReset((IODBDMAChannelRegisters*)currentChannel);
        IOSetDBDMACommandPtr(currentChannel, savedKeyLargoDBDMAState->dmaChannel[i].commandPtrLo);
        IOSetDBDMAInterruptSelect(currentChannel, savedKeyLargoDBDMAState->dmaChannel[i].interruptSelect);
        IOSetDBDMABranchSelect(currentChannel, savedKeyLargoDBDMAState->dmaChannel[i].branchSelect);
        IOSetDBDMAWaitSelect(currentChannel, savedKeyLargoDBDMAState->dmaChannel[i].waitSelect);
    }

    // Restore Audio registers.

    savedKeyLargoAudioState = &savedKeyLargState.savedAudioState;

    for (i = 0, channelOffset = 0; i < 25; i++, channelOffset += 0x0010)
    {
        *(UInt32 *) (keyLargoBaseAddr + 0x14000 + channelOffset) = savedKeyLargoAudioState->audio[i];
        eieio();
    }

    // Restore I2S registers.

    savedKeyLargoI2SState = &savedKeyLargState.savedI2SState;

    for (i = 0, channelOffset = 0; i < 10; i++, channelOffset += 0x0010)
    {
        *(UInt32 *) (keyLargoBaseAddr + 0x10000 + channelOffset) = savedKeyLargoI2SState->i2s[i];
        eieio();
        *(UInt32 *) (keyLargoBaseAddr + 0x11000 + channelOffset) = savedKeyLargoI2SState->i2s[i + 1];
        eieio();
    }

    // Restore GPIO portion of KeyLargo.

    savedKeyLargoGPIOState = &savedKeyLargState.savedGPIOState;

    *(UInt32 *)(keyLargoBaseAddr + 0x50) = savedKeyLargoGPIOState->gpioLevels[0];
    eieio();
    *(UInt32 *)(keyLargoBaseAddr + 0x54) = savedKeyLargoGPIOState->gpioLevels[1];
    eieio();

    for (i = 0; i < 18; i++)
    {
        *(UInt8 *)(keyLargoBaseAddr + 0x58 + i) = savedKeyLargoGPIOState->extIntGPIO[i];
        eieio();
    }

    for (i = 0; i < 17; i++)
    {
        *(UInt8 *)(keyLargoBaseAddr + 0x6A + i) = savedKeyLargoGPIOState->gpio[i];
        eieio();
    }
}


// Method: saveVIAState
//
// Purpose:
//        saves the state of all the meaningful registers into a local buffer.
//    this method is almost a copy and paste of the orignal MacOS9 function
//    SaveVIAState.
void KeyLargo::saveVIAState(void)
{
    UInt8* viaBase = (UInt8*)keyLargoBaseAddress + VIA_OFFSET;
    UInt8* savedKeyLargoViaState = savedKeyLargState.savedVIAState;

    // Save VIA state.  These registers don't seem to get restored to any known state.
    savedKeyLargoViaState[0] = *(UInt8*)(viaBase + vBufA);
    savedKeyLargoViaState[1] = *(UInt8*)(viaBase + vDIRA);
    savedKeyLargoViaState[2] = *(UInt8*)(viaBase + vBufB);
    savedKeyLargoViaState[3] = *(UInt8*)(viaBase + vDIRB);
    savedKeyLargoViaState[4] = *(UInt8*)(viaBase + vPCR);
    savedKeyLargoViaState[5] = *(UInt8*)(viaBase + vACR);
    savedKeyLargoViaState[6] = *(UInt8*)(viaBase + vIER);
    savedKeyLargoViaState[7] = *(UInt8*)(viaBase + vT1C);
    savedKeyLargoViaState[8] = *(UInt8*)(viaBase + vT1CH);
}


// Method: restoreVIAState
//
// Purpose:
//        restores the state of all the meaningful registers from a local buffer.
//    this method is almost a copy and paste of the orignal MacOS9 function
//    RestoreVIAState.
void KeyLargo::restoreVIAState(void)
{
    UInt8* viaBase = (UInt8*)keyLargoBaseAddress + VIA_OFFSET;
    UInt8* savedKeyLargoViaState = savedKeyLargState.savedVIAState;

    // Restore VIA state.  These registers don't seem to get restored to any known state.
    *(UInt8*)(viaBase + vBufA) = savedKeyLargoViaState[0];
    eieio();
    *(UInt8*)(viaBase + vDIRA) = savedKeyLargoViaState[1];
    eieio();
    *(UInt8*)(viaBase + vBufB) = savedKeyLargoViaState[2];
    eieio();
    *(UInt8*)(viaBase + vDIRB) = savedKeyLargoViaState[3];
    eieio();
    *(UInt8*)(viaBase + vPCR) = savedKeyLargoViaState[4];
    eieio();
    *(UInt8*)(viaBase + vACR) = savedKeyLargoViaState[5];
    eieio();
    *(UInt8*)(viaBase + vIER) = savedKeyLargoViaState[6];
    eieio();
    *(UInt8*)(viaBase + vT1C) = savedKeyLargoViaState[7];
    eieio();
    *(UInt8*)(viaBase + vT1CH) = savedKeyLargoViaState[8];
    eieio();
}


IOReturn KeyLargo::callPlatformFunction(const OSSymbol *functionName,
                                        bool waitForFunction,
                                        void *param1, void *param2,
                                        void *param3, void *param4)
{  
    if (functionName == keyLargo_resetUniNEthernetPhy)
    {
        resetUniNEthernetPhy();
        return kIOReturnSuccess;
    }

    if (functionName == keyLargo_restoreRegisterState)
    {
        restoreRegisterState();
        return kIOReturnSuccess;
    }
    
    if (functionName == keyLargo_syncTimeBase)
    {
        syncTimeBase();
        return kIOReturnSuccess;
    }

    if (functionName == keyLargo_saveRegisterState)
    {
        saveRegisterState();
        return kIOReturnSuccess;
    }

    if (functionName == keyLargo_turnOffIO)
    {
        if (keyLargoDeviceId == kKeyLargoDeviceId25)
            turnOffPangeaIO((bool)param1);
        else
            turnOffKeyLargoIO((bool)param1);
            
        return kIOReturnSuccess;
    }

    if (functionName == keyLargo_writeRegUInt8)
    {
        writeRegUInt8(*(unsigned long *)param1, (UInt8)param2);
        return kIOReturnSuccess;
    }

    if (functionName == keyLargo_safeWriteRegUInt8)
    {
        safeWriteRegUInt8((unsigned long)param1, (UInt8)param2, (UInt8)param3);
        return kIOReturnSuccess;
    }

    if (functionName == keyLargo_safeReadRegUInt8)
    {
        UInt8 *returnval = (UInt8 *)param2;
        *returnval = safeReadRegUInt8((unsigned long)param1);
        return kIOReturnSuccess;
    }

    if (functionName == keyLargo_safeWriteRegUInt32)
    {
        safeWriteRegUInt32((unsigned long)param1, (UInt32)param2, (UInt32)param3);
        return kIOReturnSuccess;
    }

    if (functionName == keyLargo_safeReadRegUInt32)
    {
        UInt32 *returnval = (UInt32 *)param2;
        *returnval = safeReadRegUInt32((unsigned long)param1);
        return kIOReturnSuccess;
    }

    if (functionName == keyLargo_powerMediaBay)
    {
        bool powerOn = (param1 != NULL);
        powerMediaBay(powerOn, (UInt8)param2);
        return kIOReturnSuccess;
    }
    
    if (functionName->isEqualTo("EnableSCC"))
    {
        EnableSCC((bool)param1, (UInt8)param2, (bool)param3);
        return kIOReturnSuccess;
    }

    if (functionName->isEqualTo("PowerModem"))
    {
        PowerModem((bool)param1);
        return kIOReturnSuccess;
    }

    if (functionName->isEqualTo("ModemResetLow"))
    {
        ModemResetLow();
        return kIOReturnSuccess;
    }

    if (functionName->isEqualTo("ModemResetHigh"))
    {
        ModemResetHigh();
        return kIOReturnSuccess;
    }
	
    if (functionName == keyLargo_getHostKeyLargo)
    {
        UInt32 *returnVal = (UInt32 *)param1;
		*returnVal = (UInt32) gHostKeyLargo;
        return kIOReturnSuccess;
    }

    return super::callPlatformFunction(functionName, waitForFunction, param1, param2, param3, param4);
}

void KeyLargo::EnableSCC(bool state, UInt8 device, bool type)
{
    UInt32 bitsToSet, bitsToClear, currentReg = 0;
    IOInterruptState intState;
        
    if (state)
    {
        bitsToSet = (1 << 6);			// Enables SCC Cell
        
        if(device == 0) // SCCA
        {
            bitsToSet |= (1 << 4);		// Enables SCC Interface A

            if(type) // I2S1
            {
                bitsToClear |= (1 << 1);	// Enables SCC Interface A to support I2S1
            }
            else // SCC
            {
                bitsToSet |= (1 << 1);		// Enables SCC Interface A to support SCCA
            }
        }
        if(device == 1) // SCCB
        {
            bitsToSet |= (1 << 5);		// Enables SCC Interface B

            if(type) // IrDA
            {                
                bitsToSet |= (1 << 17);		// irda 19.584 MHz clock
                bitsToSet |= (1 << 16);		// irda 32 mhz clock on
                bitsToSet |= (1 << 15);		// IrDA Enable
                bitsToClear |= (1 << 14);	// fast connect
                bitsToClear |= (1 << 13);	// default0
                bitsToClear |= (1 << 12);	// default1	
                bitsToSet |= (1 << 10);		// use ir source 1
                bitsToClear |= (1 << 9);	// do not use ir source 2
                bitsToClear |= (1 << 8);	// high band for 1mbit.  0 for low speed?
                //bitsToClear |= (1 << 2);	// SlowPCLK.    0 for SCCPCLK at 24.576 MHZ, 1 for 15.6672 MHz
                bitsToClear |= (1 << 0);	// Enables SCC Interface B to support IrDA
            }
            else // SCC
            {
                bitsToSet |= (1 << 0);		// Enables SCC Interface B to support SCCB
            }
        }
                
        if ( mutex  != NULL ) 			// Take Lock
            intState = IOSimpleLockLockDisableInterrupt(mutex);
  
        currentReg = readRegUInt32( (unsigned long)kKeyLargoFCR0);
        currentReg |= bitsToSet;
        currentReg &= ~bitsToClear;
        
        writeRegUInt32( (unsigned long)kKeyLargoFCR0, (UInt32)currentReg );

        // Reset the SCC
        currentReg |= (1 << 3);
        writeRegUInt32( (unsigned long)kKeyLargoFCR0, (UInt32)currentReg );

        IODelay(15000);

        currentReg &= ~(1 << 3);
        writeRegUInt32( (unsigned long)kKeyLargoFCR0, (UInt32)currentReg );

        if(device == 1 && (type))	// Reset the IrDA:
        {
            currentReg = (1 << 11);
            writeRegUInt32( (unsigned long)kKeyLargoFCR0, (UInt32)currentReg );

            IODelay(15000);

            currentReg &= ~(1 << 11);
            writeRegUInt32( (unsigned long)kKeyLargoFCR0, (UInt32)currentReg );
        }
        
        if ( mutex  != NULL )			// Release Lock
            IOSimpleLockUnlockEnableInterrupt(mutex, intState);
    }
    else
    {
        if(device == 0)
        {
            bitsToClear |= (1 << 4);		// Disables SCC A
        }
        if(device == 1)
        {
            bitsToClear |= (1 << 5);		// Disables SCC B
            
            if (type)
            {
                bitsToClear |= (1 << 17);	// irda 19.584 MHz clock
                bitsToClear |= (1 << 16);	// irda 32 mhz clock on
                bitsToClear |= (1 << 15);	// IrDA Enable
                bitsToClear |= (1 << 14);	// fast connect
                bitsToClear |= (1 << 13);	// default0
                bitsToClear |= (1 << 12);	// default1	
                bitsToClear |= (1 << 10);	// use ir source 1
                bitsToClear |= (1 << 9);	// do not use ir source 2
                bitsToClear |= (1 << 8);	// high band for 1mbit.  0 for low speed?
                //bitsToClear |= (1 << 2);	// SlowPCLK.    0 for SCCPCLK at 24.576 MHZ, 1 for 15.6672 MHz
            }
        }

        if ( mutex  != NULL ) 			// Take Lock
            intState = IOSimpleLockLockDisableInterrupt(mutex);
  
        currentReg = readRegUInt32( (unsigned long)kKeyLargoFCR0);
        currentReg |= bitsToSet;
        currentReg &= ~bitsToClear;
        
        writeRegUInt32( (unsigned long)kKeyLargoFCR0, (UInt32)currentReg );

        if ( mutex  != NULL )			// Release Lock
            IOSimpleLockUnlockEnableInterrupt(mutex, intState);
    }
    
    return;
}

void KeyLargo::PowerModem(bool state)
{
    if (keyLargoDeviceId == kKeyLargoDeviceId25)
    {
        if (state)
        {
            writeRegUInt8(kKeyLargoGPIOBase + 0x2, 0x4); // power modem on
            eieio();
        }
        else
        {
            writeRegUInt8(kKeyLargoGPIOBase + 0x2, 0x5); // power modem off
            eieio();
        }
    }
    else
    {
        if (state)
            safeWriteRegUInt32( (unsigned long)kKeyLargoFCR2, (UInt32)(1<<25), (UInt32)(0) );
        else
            safeWriteRegUInt32( (unsigned long)kKeyLargoFCR2, (UInt32)(1<<25), (UInt32)kKeyLargoFCR2AltDataOut );
    }

    return;
}

void KeyLargo::ModemResetLow()
{
        *(UInt8*)(keyLargoBaseAddress + kKeyLargoGPIOBase + 0x3) |= 0x04;	// Set GPIO3_DDIR to output
        eieio();
        *(UInt8*)(keyLargoBaseAddress + kKeyLargoGPIOBase + 0x3) &= ~0x01;	// Set GPIO3_DataOut output to zero
        eieio();
}

void KeyLargo::ModemResetHigh()
{
        *(UInt8*)(keyLargoBaseAddress + kKeyLargoGPIOBase + 0x3) |= 0x04;	// Set GPIO3_DDIR to output
        eieio();
        *(UInt8*)(keyLargoBaseAddress + kKeyLargoGPIOBase + 0x3) |= 0x01;	// Set GPIO3_DataOut output to 1
        eieio();
}

void KeyLargo::resetUniNEthernetPhy(void)
{
  // Uni-N Ethernet's Phy reset is controlled by GPIO16.
  // This should be determined from the device tree.
  
  // Pull down GPIO16 for 10ms (> 1ms) to hard reset the Phy,
  // and bring it out of low-power mode.
  writeRegUInt8(kKeyLargoGPIOBase + 16, kKeyLargoGPIOOutputEnable);
  IOSleep(10);
  writeRegUInt8(kKeyLargoGPIOBase + 16,
		kKeyLargoGPIOOutputEnable | kKeyLargoGPIOData);
  IOSleep(10);
}


#undef super
#define super IOWatchDogTimer

OSDefineMetaClassAndStructors(KeyLargoWatchDogTimer, IOWatchDogTimer);


KeyLargoWatchDogTimer *KeyLargoWatchDogTimer::withKeyLargo(KeyLargo *keyLargo)
{
  KeyLargoWatchDogTimer *watchDogTimer = new KeyLargoWatchDogTimer;
  
  if (watchDogTimer == 0) return 0;
  
  while (1) {
    if (!watchDogTimer->init()) break;
    
    watchDogTimer->attach(keyLargo);
    
    if (watchDogTimer->start(keyLargo)) return watchDogTimer;
    
    watchDogTimer->detach(keyLargo);
    break;
  }
  
  return 0;
}

bool KeyLargoWatchDogTimer::start(IOService *provider)
{
  keyLargo = OSDynamicCast(KeyLargo, provider);
  if (keyLargo == 0) return false;
  
  return super::start(provider);
}

enum {
  kKeyLargoWatchDogLow    = 0x15030,
  kKeyLargoWatchDogHigh   = 0x15034,
  kKeyLargoCounterLow     = 0x15038,
  kKeyLargoCounterHigh    = 0x1503C,
  kKeyLargoWatchDogEnable = 0x15048
};

void KeyLargoWatchDogTimer::setWatchDogTimer(UInt32 timeOut)
{
  UInt32 timeLow, timeHigh, timeHigh2, watchDogLow, watchDogHigh;
  UInt64 offset, time;
  
  if (timeOut != 0) {
    offset = (UInt64)timeOut * kKeyLargoGTimerFreq;
    
    do {
      timeHigh = keyLargo->readRegUInt32(kKeyLargoCounterHigh);
      timeLow = keyLargo->readRegUInt32(kKeyLargoCounterLow);
      timeHigh2 = keyLargo->readRegUInt32(kKeyLargoCounterHigh);
      
    } while (timeHigh != timeHigh2);
    time = (((UInt64)timeHigh) << 32) + timeLow;
    
    time += offset;
    
    watchDogLow = time & 0x0FFFFFFFFULL;
    watchDogHigh = time >> 32;
    
    keyLargo->writeRegUInt32(kKeyLargoWatchDogLow, watchDogLow);
    keyLargo->writeRegUInt32(kKeyLargoWatchDogHigh, watchDogHigh);
  }
  
  keyLargo->writeRegUInt32(kKeyLargoWatchDogEnable,
			   (timeOut != 0) ? 1 : 0);
}
