#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 "KeyLargo.h"
#define super AppleMacIO
OSDefineMetaClass(KeyLargo, AppleMacIO)
OSDefineAbstractStructors(KeyLargo, AppleMacIO)
extern "C" {
extern UInt32 TimeSystemBusKeyLargo ( IOLogicalAddress keyLargoBaseAddr );
}
bool KeyLargo::start(IOService *provider)
{
IORegistryEntry *viaPMU;
OSData *tmpData;
UInt32 pmuVersion;
IORegistryEntry *entry;
keyLargoService = provider;
entry = fromPath("/", gIODTPlane);
tmpData = OSDynamicCast(OSData, entry->getProperty("clock-frequency"));
if (tmpData == 0) return false;
busSpeed = *(unsigned long *)tmpData->getBytesNoCopy();
if (callPlatformFunction ("PlatformIsPortable", true, (void *) &hostIsMobile, (void *)0,
(void *)0, (void *)0) != kIOReturnSuccess)
hostIsMobile = false;
if (!super::start(provider))
return false;
mutex = IOSimpleLockAlloc();
if (mutex != NULL)
IOSimpleLockInit( mutex );
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();
AdjustBusSpeeds ( );
syncTimeBase();
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);
}
}
}
IOService *grandParent = provider->getProvider()->getProvider();
OSIterator *iterator = grandParent->getChildIterator(gIODTPlane);
IORegistryEntry *peer;
OSData *clockID;
fBaseUSBID = kMaxNumUSB;
while (peer = (IORegistryEntry *)iterator->getNextObject()) {
clockID = OSDynamicCast(OSData, peer->getProperty("AAPL,clock-id"));
if(strcmp (peer->getName(), "usb") == 0 && clockID) {
UInt8 id;
id = ((UInt8 *)clockID->getBytesNoCopy())[3] - '0';
if(id < fBaseUSBID)
fBaseUSBID = id;
fNumUSB++;
}
}
iterator->release();
fHasSoftModem = false;
entry = fromPath("mac-io/i2s/i2s-b", gIODTPlane);
if(entry) {
OSData *data = OSDynamicCast(OSData, entry->getProperty("compatible"));
if(data) {
fHasSoftModem = strncmp("i2s-modem", (const char *)data->getBytesNoCopy(), data->getLength()) == 0;
}
}
return true;
}
long long KeyLargo::syncTimeBase(void)
{
UInt32 cnt;
UInt32 gtLow, gtHigh, gtHigh2;
UInt32 tbLow, tbHigh, tbHigh2;
long long tmp, diffTicks, ratioLow, ratioHigh;
tmp = gPEClockFrequencyInfo.timebase_frequency_hz;
ratioLow = (tmp << 32) / (kKeyLargoGTimerFreq);
ratioHigh = ratioLow >> 32;
ratioLow &= 0xFFFFFFFFULL;
do {
tbHigh = mftbu();
tbLow = mftb();
tbHigh2 = mftbu();
} while (tbHigh != tbHigh2);
diffTicks = ((long long)tbHigh << 32) | tbLow;
for (cnt = 0; cnt < 2; cnt++) {
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::recalibrateBusSpeeds(void)
{
AdjustBusSpeeds();
}
UInt8 KeyLargo::readRegUInt8(unsigned long offset)
{
return *(UInt8 *)(keyLargoBaseAddress + offset);
}
void KeyLargo::writeRegUInt8(unsigned long offset, UInt8 data)
{
*(UInt8 *)(keyLargoBaseAddress + offset) = data;
eieio();
return;
}
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);
return;
}
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();
return;
}
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);
}
void KeyLargo::saveVIAState(UInt8* savedK2ViaState)
{
UInt8* viaBase = (UInt8*)keyLargoBaseAddress + kKeyLargoVIABaseOffset;
savedK2ViaState[0] = *(UInt8*)(viaBase + kKeyLargovBufA);
savedK2ViaState[1] = *(UInt8*)(viaBase + kKeyLargovDIRA);
savedK2ViaState[2] = *(UInt8*)(viaBase + kKeyLargovBufB);
savedK2ViaState[3] = *(UInt8*)(viaBase + kKeyLargovDIRB);
savedK2ViaState[4] = *(UInt8*)(viaBase + kKeyLargovPCR);
savedK2ViaState[5] = *(UInt8*)(viaBase + kKeyLargovACR);
savedK2ViaState[6] = *(UInt8*)(viaBase + kKeyLargovIER);
savedK2ViaState[7] = *(UInt8*)(viaBase + kKeyLargovT1C);
savedK2ViaState[8] = *(UInt8*)(viaBase + kKeyLargovT1CH);
return;
}
void KeyLargo::restoreVIAState(UInt8* savedK2ViaState)
{
UInt8* viaBase = (UInt8*)keyLargoBaseAddress + kKeyLargoVIABaseOffset;
*(UInt8*)(viaBase + kKeyLargovBufA) = savedK2ViaState[0];
eieio();
*(UInt8*)(viaBase + kKeyLargovDIRA) = savedK2ViaState[1];
eieio();
*(UInt8*)(viaBase + kKeyLargovBufB) = savedK2ViaState[2];
eieio();
*(UInt8*)(viaBase + kKeyLargovDIRB) = savedK2ViaState[3];
eieio();
*(UInt8*)(viaBase + kKeyLargovPCR) = savedK2ViaState[4];
eieio();
*(UInt8*)(viaBase + kKeyLargovACR) = savedK2ViaState[5];
eieio();
*(UInt8*)(viaBase + kKeyLargovIER) = savedK2ViaState[6];
eieio();
*(UInt8*)(viaBase + kKeyLargovT1C) = savedK2ViaState[7];
eieio();
*(UInt8*)(viaBase + kKeyLargovT1CH) = savedK2ViaState[8];
eieio();
return;
}
void KeyLargo::AdjustBusSpeeds ( void )
{
IOInterruptState is;
IOSimpleLock *intLock;
UInt32 ticks;
UInt64 systemBusHz;
#if 0
UInt64 tmp64;
#endif
intLock = IOSimpleLockAlloc();
if (intLock) {
IOSimpleLockInit (intLock);
is = IOSimpleLockLockDisableInterrupt(intLock); }
ticks = TimeSystemBusKeyLargo (keyLargoBaseAddress);
if (intLock) {
IOSimpleLockUnlockEnableInterrupt(intLock, is); IOSimpleLockFree (intLock);
}
systemBusHz = 4194300;
systemBusHz *= 18432000;
systemBusHz /= ticks;
#if 0
kprintf ("KeyLargo::AdjustBusSpeeds - ticks = %ld, new systemBusHz = %ld, old systemBusHz = %ld\n", ticks,
(UInt32)(systemBusHz & 0xFFFFFFFF), (UInt32)(gPEClockFrequencyInfo.bus_clock_rate_hz & 0xFFFFFFFF));
kprintf ("old clock frequency values:\n");
kprintf (" bus_clock_rate_hz: %ld\n", gPEClockFrequencyInfo.bus_clock_rate_hz);
kprintf (" cpu_clock_rate_hz: %ld\n", gPEClockFrequencyInfo.cpu_clock_rate_hz);
kprintf (" dec_clock_rate_hz: %ld\n", gPEClockFrequencyInfo.dec_clock_rate_hz);
kprintf (" bus_clock_rate_num: %ld\n", gPEClockFrequencyInfo.bus_clock_rate_num);
kprintf (" bus_clock_rate_den: %ld\n", gPEClockFrequencyInfo.bus_clock_rate_den);
kprintf (" bus_to_cpu_rate_num: %ld\n", gPEClockFrequencyInfo.bus_to_cpu_rate_num);
kprintf (" bus_to_cpu_rate_den: %ld\n", gPEClockFrequencyInfo.bus_to_cpu_rate_den);
kprintf (" bus_to_dec_rate_num: %ld\n", gPEClockFrequencyInfo.bus_to_dec_rate_num);
kprintf (" bus_to_dec_rate_den: %ld\n", gPEClockFrequencyInfo.bus_to_dec_rate_den);
kprintf (" timebase_frequency_hz: %ld\n", gPEClockFrequencyInfo.timebase_frequency_hz);
kprintf (" timebase_frequency_num: %ld\n", gPEClockFrequencyInfo.timebase_frequency_num);
kprintf (" timebase_frequency_den: %ld\n", gPEClockFrequencyInfo.timebase_frequency_den);
#endif
#if 0
tmp64 = systemBusHz;
tmp64 /= gPEClockFrequencyInfo.bus_clock_rate_den;
gPEClockFrequencyInfo.bus_clock_rate_num = (UInt32) (tmp64 & 0xFFFFFFFF);
gPEClockFrequencyInfo.bus_clock_rate_hz = (UInt32) (systemBusHz & 0xFFFFFFFF);
gPEClockFrequencyInfo.dec_clock_rate_hz = (UInt32) ((systemBusHz & 0xFFFFFFFF) / 4);
#endif
gPEClockFrequencyInfo.timebase_frequency_hz = (UInt32) ((systemBusHz & 0xFFFFFFFF) / 4);
gPEClockFrequencyInfo.timebase_frequency_num = gPEClockFrequencyInfo.timebase_frequency_hz;
gPEClockFrequencyInfo.timebase_frequency_den = 1;
#if 0
kprintf ("new clock frequency values:\n");
kprintf (" bus_clock_rate_hz: %ld\n", gPEClockFrequencyInfo.bus_clock_rate_hz);
kprintf (" cpu_clock_rate_hz: %ld\n", gPEClockFrequencyInfo.cpu_clock_rate_hz);
kprintf (" dec_clock_rate_hz: %ld\n", gPEClockFrequencyInfo.dec_clock_rate_hz);
kprintf (" bus_clock_rate_num: %ld\n", gPEClockFrequencyInfo.bus_clock_rate_num);
kprintf (" bus_clock_rate_den: %ld\n", gPEClockFrequencyInfo.bus_clock_rate_den);
kprintf (" bus_to_cpu_rate_num: %ld\n", gPEClockFrequencyInfo.bus_to_cpu_rate_num);
kprintf (" bus_to_cpu_rate_den: %ld\n", gPEClockFrequencyInfo.bus_to_cpu_rate_den);
kprintf (" bus_to_dec_rate_num: %ld\n", gPEClockFrequencyInfo.bus_to_dec_rate_num);
kprintf (" bus_to_dec_rate_den: %ld\n", gPEClockFrequencyInfo.bus_to_dec_rate_den);
kprintf (" timebase_frequency_hz: %ld\n", gPEClockFrequencyInfo.timebase_frequency_hz);
kprintf (" timebase_frequency_num: %ld\n", gPEClockFrequencyInfo.timebase_frequency_num);
kprintf (" timebase_frequency_den: %ld\n", gPEClockFrequencyInfo.timebase_frequency_den);
#endif
PE_call_timebase_callback ();
return;
}
bool KeyLargo::publishChildren(IOService * driver, IOService *(*createChildNub)(IORegistryEntry *))
{
OSIterator * kids;
IORegistryEntry * next;
IOService * provider;
provider = OSDynamicCast(IOService, driver->getParentEntry(gIOServicePlane));
if (!provider) {
return false;
}
kids = IODTFindMatchingEntries(provider, kIODTExclusive, deleteList() );
if (kids) {
while (next = (IORegistryEntry *)kids->getNextObject())
{
publishChild(driver, next, createChildNub);
}
kids->release();
}
return true;
}
bool KeyLargo::publishChild(IOService * driver, IORegistryEntry *child,
IOService *(*createChildNub)(IORegistryEntry *))
{
IOService * nub;
if (createChildNub)
nub = createChildNub(child);
else
nub = createNub(child);
if (0 == nub)
return false;
nub->attach( driver );
processNub(nub);
nub->registerService();
return true;
}