PPCI2CInterface.cpp [plain text]
#include "PPCI2CInterface.h"
#include <IOKit/IODeviceTreeSupport.h>
extern "C" vm_offset_t ml_io_map(vm_offset_t phys_addr, vm_size_t size);
#define super IOService
OSDefineMetaClassAndStructors( PPCI2CInterface, IOService )
#define I2C_REGISTERDELAY 10
#if 1
#define INLINE inline
#else
#define INLINE
#endif
INLINE void
PPCI2CInterface::dumpI2CRegisters()
{
#ifdef DEBUGMODE
IOLog("mode 0x%08lx -> 0x%02x\n", (UInt32)mode,*mode);
IOLog("control 0x%08lx -> 0x%02x\n", (UInt32)control,*control);
IOLog("status 0x%08lx -> 0x%02x\n", (UInt32)status,*status);
IOLog("ISR 0x%08lx -> 0x%02x\n", (UInt32)ISR,*ISR);
IOLog("IER 0x%08lx -> 0x%02x\n", (UInt32)IER,*IER);
IOLog("address 0x%08lx -> 0x%02x\n", (UInt32)address,*address);
IOLog("subAddr 0x%08lx -> 0x%02x\n", (UInt32)subAddr,*subAddr);
#endif //DEBUGMODE
}
INLINE void
PPCI2CInterface::SetI2CBase(UInt8 *baseAddress, UInt8 steps)
{
I2CRegister base;
base = (I2CRegister)baseAddress;
if (base != NULL) {
mode = base; control = mode + steps; status = control + steps; ISR = status + steps; IER = ISR + steps; address = IER + steps; subAddr = address + steps; data = subAddr + steps;
*status = 0x00;
*ISR = 0x00;
getMode();
#ifdef DEBUGMODE
IOLog("PPCI2CInterface::SetI2CBase(0x%08lx,0x%02x)\n",(UInt32)baseAddress, steps);
dumpI2CRegisters();
#endif // DEBUGMODE
}
}
INLINE UInt8
PPCI2CInterface::shiftedMask(UInt8 mask, UInt8 shift)
{
return (mask << shift);
}
INLINE UInt8
PPCI2CInterface::shiftedCompMask(UInt8 mask, UInt8 shift)
{
return ~shiftedMask(mask, shift);
}
INLINE void
PPCI2CInterface::writeRegisterField(I2CRegister reg, UInt8 mask, UInt8 shift, UInt8 newData)
{
UInt8 registerValue = *reg;
UInt8 data = (newData & mask);
UInt8 nMask = shiftedCompMask(mask, shift);
*reg = (registerValue & nMask) | (data << shift);
eieio();
IODelay(I2C_REGISTERDELAY);
#ifdef DEBUGMODE
IOLog("PPCI2CInterface::writeRegisterField(0x%08lx, 0x%02x, 0x%02x, 0x%02x)\n",(UInt32)reg, mask, shift, newData);
IOLog("PPCI2CInterface::writeRegisterField() registerValue=0x%02x\n", registerValue);
IOLog("PPCI2CInterface::writeRegisterField() data=0x%02x\n", data);
IOLog("PPCI2CInterface::writeRegisterField() nMask=0x%02x\n", nMask);
IOLog("PPCI2CInterface::writeRegisterField() *reg=0x%02x\n", *reg);
#endif // DEBUGMODE
}
INLINE UInt8
PPCI2CInterface::readRegisterField(I2CRegister reg, UInt8 mask, UInt8 shift)
{
UInt8 registerValue = *reg;
UInt8 returnValue = ((registerValue) >> shift) & mask;
#ifdef DEBUGMODE
IOLog("PPCI2CInterface::readRegisterField(0x%08lx, 0x%02x, 0x%02x)\n",(UInt32)reg, mask, shift);
IOLog("PPCI2CInterface::readRegisterField() *reg=0x%02x\n", *reg);
IOLog("PPCI2CInterface::readRegisterField() returnValue=0x%02x\n", returnValue);
#endif // DEBUGMODE
return returnValue;
}
INLINE void
PPCI2CInterface::setPort(UInt8 newPort)
{
portSelect = newPort;
if (!i2cPMU)
writeRegisterField(mode, (UInt8)kPortMask, (UInt8)I2CPortShift, (UInt8)newPort);
}
INLINE UInt8
PPCI2CInterface::getPort()
{
if (!i2cPMU)
portSelect = (I2CMode)readRegisterField(mode, (UInt8)kPortMask, (UInt8)I2CPortShift);
return portSelect;
}
INLINE void
PPCI2CInterface::setMode(I2CMode newMode)
{
lastMode = newMode;
if (!i2cPMU)
writeRegisterField(mode, (UInt8)kModeMask, (UInt8)I2CModeShift, (UInt8)newMode);
}
INLINE PPCI2CInterface::I2CMode
PPCI2CInterface::getMode()
{
if (!i2cPMU)
lastMode = (I2CMode)readRegisterField(mode, (UInt8)kModeMask, (UInt8)I2CModeShift);
return lastMode;
}
INLINE void
PPCI2CInterface::setSpeed(I2CSpeed newSpeed)
{
if (!i2cPMU)
writeRegisterField(mode, (UInt8)kSpeedMask, (UInt8)I2CSpeedShift, (UInt8)newSpeed);
else
lastSpeed = newSpeed;
}
INLINE PPCI2CInterface::I2CSpeed
PPCI2CInterface::getSpeed()
{
if (!i2cPMU)
return( (I2CSpeed)readRegisterField(mode, (UInt8)kSpeedMask, (UInt8)I2CSpeedShift) );
else
return lastSpeed;
}
INLINE void
PPCI2CInterface::setControl(I2CControl newControlValue)
{
*control = (UInt8)newControlValue;
eieio();
IODelay(I2C_REGISTERDELAY);
}
INLINE PPCI2CInterface::I2CControl
PPCI2CInterface::getControl()
{
I2CControl controlValue;
controlValue = (I2CControl)(*control);
return controlValue;
}
INLINE void
PPCI2CInterface::setStatus(I2CStatus newStatusValue)
{
*status = (UInt8)newStatusValue;
eieio();
IODelay(I2C_REGISTERDELAY);
}
INLINE PPCI2CInterface::I2CStatus
PPCI2CInterface::getStatus()
{
I2CStatus statusValue;
statusValue = (I2CStatus)(*status);
return statusValue;
}
INLINE void
PPCI2CInterface::setInterruptStatus(I2CInterruptStatus newStatusValue)
{
*ISR = (UInt8)newStatusValue;
eieio();
IODelay(I2C_REGISTERDELAY);
}
INLINE PPCI2CInterface::I2CInterruptStatus
PPCI2CInterface::getInterruptStatus()
{
I2CInterruptStatus intStatus;
intStatus = (I2CInterruptStatus)(*ISR);
return intStatus;
}
INLINE void
PPCI2CInterface::setInterruptEnable(I2CInterruptEnable newInterruptEnable)
{
*IER = (UInt8)newInterruptEnable;
eieio();
IODelay(I2C_REGISTERDELAY);
}
INLINE PPCI2CInterface::I2CInterruptEnable
PPCI2CInterface::setInterruptEnable()
{
I2CInterruptEnable interEnable;
interEnable = (I2CInterruptEnable)(*IER);
return interEnable;
}
INLINE void
PPCI2CInterface::setAddress(UInt8 newAddress)
{
writeRegisterField(address, (UInt8)kADDRMask, (UInt8)I2CAddressShift, (UInt8)newAddress);
eieio();
}
INLINE void
PPCI2CInterface::setAddressRegister(UInt8 newAddress, I2CRWMode readMode)
{
newAddress &= kADDRMask;
if (i2cPMU) {
currentAddress = (newAddress << I2CAddressShift) | readMode;
}
else {
*address = (newAddress << I2CAddressShift) | readMode;
IODelay(I2C_REGISTERDELAY);
#ifdef DEBUGMODE
IOLog("setAddressRegister( 0x%02x, %d) = 0x%02x\n", newAddress, (UInt8)readMode, (UInt8)*address);
#endif // DEBUGMODE
eieio();
}
}
INLINE UInt8
PPCI2CInterface::getAddress()
{
return readRegisterField(address, (UInt8)kADDRMask, (UInt8)I2CAddressShift);
}
INLINE void
PPCI2CInterface::setReadWrite(I2CRWMode readMode)
{
writeRegisterField(address, (UInt8)kRWMask, (UInt8)I2CRWShift, (UInt8)readMode);
eieio();
}
INLINE PPCI2CInterface::I2CRWMode
PPCI2CInterface::getReadWrite()
{
return (I2CRWMode)readRegisterField(address, (UInt8)kRWMask, (UInt8)I2CRWShift);
}
INLINE void
PPCI2CInterface::setSubAddress(UInt8 newSubAddress)
{
*subAddr = newSubAddress;
eieio();
}
INLINE UInt8
PPCI2CInterface::getSubAddress()
{
return (*subAddr);
}
INLINE void
PPCI2CInterface::setData(UInt8 newByte)
{
*data = newByte;
eieio();
IODelay(I2C_REGISTERDELAY);
}
INLINE UInt8
PPCI2CInterface::getData()
{
return (*data);
}
INLINE bool
PPCI2CInterface::setAddressAndDirection()
{
transferWasSuccesful = true;
if (isReading) {
setAddressRegister(currentAddress, kReadADDR);
}
else {
setAddressRegister(currentAddress, kWriteADDR);
}
if (i2cPMU)
return(true);
if ((lastMode == kStandardSubMode) || (lastMode == kCombinedMode))
setSubAddress(currentSubaddress);
setInterruptStatus(kISRMask); currentState = ki2cStateWaitingForIADDR;
setControl((I2CControl)(getControl() | kXAddrCNTRL));
#ifdef DEBUGMODE
IOLog("PPCI2CInterface::setAddressAndDirection()\n");
#endif // DEBUGMODE
return(true);
}
bool
PPCI2CInterface::i2cStandardSubModeInterrupts(UInt8 interruptStatus)
{
bool success = true;
switch (currentState) {
case ki2cStateWaitingForIADDR:
#ifdef DEBUGMODE
IOLog("ki2cStateWaitingForIADDR: ");
#endif // DEBUGMODE
if ((interruptStatus & kIAddrISR) == 0) {
#ifdef DEBUGMODE
IOLog("PPCI2CInterface::i2cStandardSubModeInterrupts (ki2cStateWaitingForIADDR): bad state ");
#endif
success = false;
}
if (success) {
if (getStatus() & kLastAakSTATUS) {
if (isReading) {
if (nBytes > 1)
setControl((I2CControl)(getControl() | kAakCNTRL));
}
else {
setData(*dataBuffer++);
nBytes--;
}
currentState = ki2cStateWaitingForIDATA;
}
else {
#ifdef DEBUGMODE
IOLog("ki2cStateWaitingForIADDR...no slave...");
#endif
currentState = ki2cStateWaitingForISTOP;
success = false;
}
}
else {
abortTransfer();
}
setInterruptStatus(kIAddrISR);
break;
case ki2cStateWaitingForIDATA:
#ifdef DEBUGMODE
IOLog("ki2cStateWaitingForIDATA: ");
#endif
if ((interruptStatus & kIDataISR) == 0) {
#ifdef DEBUGMODE
IOLog("PPCI2CInterface::i2cStandardSubModeInterrupts (ki2cStateWaitingForIDATA): bad state ");
#endif
success = false;
}
if (success) {
if (isReading) {
*dataBuffer++ = getData();
nBytes--;
if (nBytes == 0) currentState = ki2cStateWaitingForISTOP;
else if (nBytes > 1)
setControl((I2CControl)(getControl() | kAakCNTRL));
else setControl(kClrCNTRL); }
else {
if (getStatus() & kLastAakSTATUS) {
if (nBytes == 0) {
currentState = ki2cStateWaitingForISTOP;
setControl((I2CControl)(getControl() | kStopCNTRL));
}
else {
setData(*dataBuffer++);
nBytes--;
}
}
else {
#ifdef DEBUGMODE
IOLog("ki2cStateWaitingForIDATA...no slave...");
#endif
currentState = ki2cStateWaitingForISTOP;
success = false;
}
}
}
else {
abortTransfer();
}
setInterruptStatus(kIDataISR);
break;
case ki2cStateWaitingForISTOP:
#ifdef DEBUGMODE
IOLog("ki2cStateWaitingForISTOP: ");
#endif
if ((interruptStatus & kIStopISR) == 0) {
#ifdef DEBUGMODE
IOLog("PPCI2CInterface::i2cStandardSubModeInterrupts (ki2cStateWaitingForISTOP): bad state ");
#endif
success = false;
}
setInterruptStatus(kIStopISR);
currentState = ki2cStateIdle;
semaphore_signal(mySync);
break;
case ki2cStateWaitingForISTART:
#ifdef DEBUGMODE
IOLog("ki2cStateWaitingForISTART: is not supposed to happen in this state machine ");
#endif
break;
case ki2cStateIdle:
#ifdef DEBUGMODE
IOLog("ki2cStateIdle: ");
#endif
break;
default:
break;
}
#ifdef DEBUGMODE
IOLog(" %s\n", (success ? "true" : "false"));
#endif
return success;
}
INLINE bool
PPCI2CInterface::abortTransfer()
{
currentState = ki2cStateWaitingForISTOP;
setControl((I2CControl)(getControl() | kStopCNTRL));
return false;
}
bool
PPCI2CInterface::waitForCompletion()
{
UInt16 loop = 15000;
UInt8 intStat = getInterruptStatus();
if (intStat & kISRMask)
handleI2CInterrupt();
while((loop--) && (currentState != ki2cStateIdle)) {
if (ml_at_interrupt_context())
IODelay (1000);
else
IOSleep(1);
intStat = getInterruptStatus();
if (intStat & kISRMask)
handleI2CInterrupt();
}
if (currentState != ki2cStateIdle) {
#ifdef DEBUGMODE
IOLog("PPCI2CInterface::waitForCompletion error loop is incomplete\n");
#endif
return false;
}
else {
#ifdef DEBUGMODE
IOLog("PPCI2CInterface::waitForCompletion loop is complete\n");
#endif
return true;
}
}
bool
PPCI2CInterface::setKhzSpeed(UInt speed)
{
switch (speed)
{
case 100:
setSpeed(k100KhzMode);
break;
case 50:
setSpeed(k50KhzMode);
break;
case 25:
setSpeed(k25KhzMode);
break;
default:
#ifdef DEBUGMODE
IOLog("PPCI2CInterface::setKhzSpeed Can not set bus speed %d is not allowed\n", speed);
#endif
return false;
break;
}
return true;
}
bool
PPCI2CInterface::initI2CBus(UInt8 *baseAddress, UInt8 steps)
{
pollingMode = true;
currentState = ki2cStateIdle;
SetI2CBase(baseAddress, steps);
return true;
}
void
PPCI2CInterface::handleHardwareInterrupt(OSObject *target, void *refCon, IOService *nub, int source)
{
PPCI2CInterface *ppcI2C = OSDynamicCast(PPCI2CInterface, target);
if (ppcI2C != NULL) {
ppcI2C->myProvider->disableInterrupt(0);
ppcI2C->handleI2CInterrupt();
ppcI2C->myProvider->enableInterrupt(0);
}
}
bool
PPCI2CInterface::handleI2CInterrupt()
{
bool success = false;
switch(lastMode)
{
case kDumbMode:
#ifdef DEBUGMODE
IOLog("PPCI2CInterface::handleI2CInterrupt kDumbMode is an unsupported mode (for now)\n");
#endif
break;
case kStandardMode:
case kStandardSubMode:
case kCombinedMode:
success = i2cStandardSubModeInterrupts(getInterruptStatus());
break;
}
transferWasSuccesful = (transferWasSuccesful && success);
#ifdef DEBUGMODE
IOLog("PPCI2CInterface::handleI2CInterrupt transferWasSuccesful = %s success= %s.\n",
((transferWasSuccesful) ? "true" : "false"),
(success ? "true" : "false"));
#endif // DEBUGMODE
return success;
}
bool
PPCI2CInterface::start(IOService *provider)
{
OSData *t;
UInt32 baseAddress;
UInt32 addressSteps;
UInt32 rate;
OSIterator *iterator;
IORegistryEntry *registryEntry;
IOService *nub;
i2cRegisterMap = NULL;
if (!super::start(provider)) return false;
retrieveProperty(provider);
mutexLock = IORecursiveLockAlloc();
if (mutexLock == NULL) {
#ifdef DEBUGMODE
IOLog("PPCI2CInterface::start can not create the IORecursiveLock so we bail off\n");
#endif
return false;
}
IORecursiveLockLock(mutexLock);
if (i2cUniN == TRUE)
{
if( (iterator = provider->getChildIterator(gIODTPlane)) )
{
while( (registryEntry = (IORegistryEntry *)iterator->getNextObject()) != 0)
{
if ((nub = new PPCI2CInterface) !=0)
{
if(!nub->init(registryEntry, gIODTPlane) ) {
nub->free();
nub = 0;
}
else
{
nub->attach(this);
nub->registerService();
}
}
}
iterator->release();
}
}
else if (provider->getProperty("preserveIODeviceTree") != 0)
provider->callPlatformFunction("mac-io-publishChildren",0,(void*)this,
(void*)0,(void*)0,(void*)0);
#ifdef DEBUGMODE
IOLog("PPCI2CInterface::start(%s)\n", provider->getName());
#endif
#if 1
if (!i2cPMU) {
t = OSDynamicCast(OSData, provider->getProperty("AAPL,address"));
if (t != NULL) {
baseAddress = *((UInt32*)t->getBytesNoCopy());
baseAddress = ml_io_map(baseAddress, 0x1000);
}
else {
#ifdef DEBUGMODE
IOLog( "PPCI2CInterface::start missing property AAPL,address in i2c registry\n");
#endif
return false;
}
#else
i2cRegisterMap = provider->mapDeviceMemoryWithIndex(1);
if ( i2cRegisterMap == NULL ) {
#ifdef DEBUGMODE
IOLog( "PPCI2CInterface::start missing i2cRegisterMap\n");
#endif
return false;
}
else
baseAddress = (UInt32)pmap_extract(kernel_pmap,(vm_address_t)i2cRegisterMap->getVirtualAddress());
#endif
t = OSDynamicCast(OSData, provider->getProperty("AAPL,address-step"));
if (t != NULL)
addressSteps = *((UInt32*)t->getBytesNoCopy());
else {
#ifdef DEBUGMODE
IOLog( "PPCI2CInterface::start missing property AAPL,address-step in i2c registry\n");
#endif
return false;
}
t = OSDynamicCast(OSData, provider->getProperty("AAPL,i2c-rate"));
if (t != NULL)
rate = *((UInt32*)t->getBytesNoCopy());
else {
#ifdef DEBUGMODE
IOLog( "PPCI2CInterface::start missing property AAPL,i2c-rate in i2c registry\n");
#endif
return false;
}
if (!initI2CBus((UInt8*)baseAddress, (UInt8)addressSteps))
return false;
}
if (i2cPMU) {
setKhzSpeed(100); clientMutexLock = NULL; clientMutexLock = IOLockAlloc();
}
else if (!setKhzSpeed((UInt)rate))
return false;
myProvider = provider;
pollingMode = true;
publishResource(getResourceName(), this );
IORecursiveLockUnlock(mutexLock);
return true;
}
bool PPCI2CInterface::retrieveProperty(IOService *provider)
{
i2cPMU = false;
i2cUniN = false;
i2cmacio = false;
OSData *propertyPtr = 0;
IOService *nub = provider;
registeredForPmuI2C = false;
nub = nub->getProvider();
if ( (propertyPtr = OSDynamicCast(OSData, provider->getProperty("name"))) )
{
if ( strncmp("pmu-i2c", (const char *)propertyPtr->getBytesNoCopy(), 7) == 0 )
{
#ifdef DEBUGPMU
IOLog(" APPLE I2C: pmu-i2c COMPATIBLE !\n");
#endif i2cPMU = true;
return true;
}
}
if ( (propertyPtr = OSDynamicCast(OSData, provider->getProperty("AAPL,driver-name"))) )
{
if ( strncmp(".i2c-uni-n", (const char *)propertyPtr->getBytesNoCopy(), 10) == 0 )
{
#ifdef DEBUGPMU
IOLog(" APPLE I2C: UniN-i2c COMPATIBLE !\n");
#endif i2cUniN = true;
return true;
}
}
i2cmacio = true;
return true;
}
void
PPCI2CInterface::free()
{
if (mutexLock != NULL)
IORecursiveLockFree(mutexLock);
if (i2cRegisterMap != NULL)
i2cRegisterMap->release();
if (i2cPMU) {
if (clientMutexLock != NULL)
{ IOLockFree (clientMutexLock);
clientMutexLock = NULL;
}
clearI2CclientList();
}
}
bool
PPCI2CInterface::setPollingMode(bool newPollingMode)
{
#ifdef DEBUGMODE
newPollingMode = true;
#endif // DEBUGMODE
if (!IORecursiveLockHaveLock(mutexLock)) {
#ifdef DEBUGMODE
IOLog("PPCI2CInterface::setPollingMode(%s) I am not the owner of the lock returns without doing anything.\n",
(newPollingMode ? "true" : "false"));
#endif // DEBUGMODE
return false;
}
if (!pollingMode) {
myProvider->disableInterrupt(0);
myProvider->unregisterInterrupt(0);
}
pollingMode = newPollingMode;
if (!newPollingMode) {
if (myProvider->registerInterrupt(0, this, handleHardwareInterrupt, 0 ) != kIOReturnSuccess) {
IOLog("AppleI2C: setting hardware INTERRUPT failed; using POLLED mode instead.\n");
pollingMode = true;
return false;
}
}
return true;
}
void
PPCI2CInterface::setDumbMode()
{
if (!IORecursiveLockHaveLock(mutexLock)) {
#ifdef DEBUGMODE
IOLog("PPCI2CInterface::setDumbMode I am not the owner of the lock returns without doing anything.\n");
#endif // DEBUGMODE
return;
}
if (i2cPMU)
setMode(kSimpleI2CStream);
else
setMode(kDumbMode);
}
void
PPCI2CInterface::setStandardMode()
{
if (!IORecursiveLockHaveLock(mutexLock)) {
#ifdef DEBUGMODE
IOLog("PPCI2CInterface::setStandardMode I am not the owner of the lock returns without doing anything.\n");
#endif // DEBUGMODE
return;
}
if (i2cPMU)
setMode(kSimpleI2CStream);
else
setMode(kStandardMode);
}
void
PPCI2CInterface::setStandardSubMode()
{
if (!IORecursiveLockHaveLock(mutexLock)) {
#ifdef DEBUGMODE
IOLog("PPCI2CInterface::setStandardSubMode I am not the owner of the lock returns without doing anything.\n");
#endif // DEBUGMODE
return;
}
if (i2cPMU)
setMode(kSubaddressI2CStream);
else
setMode(kStandardSubMode);
}
void
PPCI2CInterface::setCombinedMode()
{
if (!IORecursiveLockHaveLock(mutexLock)) {
#ifdef DEBUGMODE
IOLog("PPCI2CInterface::setCombinedMode I am not the owner of the lock returns without doing anything.\n");
#endif // DEBUGMODE
return;
}
if (i2cPMU)
setMode(kCombinedI2CStream);
else
setMode(kCombinedMode);
}
bool
PPCI2CInterface::isInDumbMode()
{
if (!IORecursiveLockHaveLock(mutexLock)) {
#ifdef DEBUGMODE
IOLog("PPCI2CInterface::isInDumbMode I am not the owner of the lock returns without doing anything.\n");
#endif // DEBUGMODE
return false;
}
return (getMode() == kDumbMode);
}
bool
PPCI2CInterface::isInStandardMode()
{
if (!IORecursiveLockHaveLock(mutexLock)) {
#ifdef DEBUGMODE
IOLog("PPCI2CInterface::isInStandardMode I am not the owner of the lock returns without doing anything.\n");
#endif // DEBUGMODE
return false;
}
return (getMode() == kStandardMode);
}
bool
PPCI2CInterface::isInStandardSubMode()
{
if (!IORecursiveLockHaveLock(mutexLock)) {
#ifdef DEBUGMODE
IOLog("PPCI2CInterface::isInStandardSubMode I am not the owner of the lock returns without doing anything.\n");
#endif // DEBUGMODE
return false;
}
return (getMode() == kStandardSubMode);
}
bool
PPCI2CInterface::isInCombinedMode()
{
if (!IORecursiveLockHaveLock(mutexLock)) {
#ifdef DEBUGMODE
IOLog("PPCI2CInterface::isInCombinedMode I am not the owner of the lock returns without doing anything.\n");
#endif // DEBUGMODE
return false;
}
return (getMode() == kCombinedMode);
}
UInt
PPCI2CInterface::getKhzSpeed()
{
if (!IORecursiveLockHaveLock(mutexLock)) {
#ifdef DEBUGMODE
IOLog("PPCI2CInterface::getKhzSpeed I am not the owner of the lock returns without doing anything.\n");
#endif // DEBUGMODE
return 0;
}
switch((int)getSpeed())
{
case k100KhzMode:
return (100);
break;
case k50KhzMode:
return (50);
break;
case k25KhzMode:
return (25);
break;
}
return (0);
}
bool
PPCI2CInterface::openI2CBus(UInt8 port)
{
#ifdef DEBUGMODE
IOLog("PPCI2CInterface::openI2CBus attempting to access to PPCI2CInterface->mutexLock.\n");
IORecursiveLockLock(mutexLock);
IOLog("PPCI2CInterface::openI2CBus PPCI2CInterface->mutexLock locked.\n");
#else // !DEBUGMODE
IORecursiveLockLock(mutexLock);
#endif // DEBUGMODE
if (i2cPMU) {
lastMode = kSimpleI2CStream; #ifdef DEBUGPMU
IOLog(" APPLE I2C-PMU Try to open I2CBus..................port = 0x%2x, mode = 0x%2x\n", port, lastMode);
#endif // DEBUGPMU
}
setPort(port);
if (!i2cPMU) {
setPollingMode(true);
setInterruptEnable((I2CInterruptEnable)(kEDataIER | kEAddrIER | kEStopIER | kEStartIER));
}
else pollingMode = true;
return true;
}
bool
PPCI2CInterface::writeI2CBus(UInt8 address, UInt8 subAddress, UInt8 *newData, UInt16 len)
{
mach_timespec_t timeout;
if (!ml_at_interrupt_context())
if (!IORecursiveLockHaveLock(mutexLock)) {
#if DEBUGMODE
IOLog("PPCI2CInterface::writeI2CBus I am not the owner of the lock returns without doing anything.\n");
#endif return false;
}
bool success = true;
dataBuffer = newData;
nBytes = len;
currentAddress = address;
currentSubaddress = subAddress;
isReading = false;
if (pollingMode) {
if ((success = setAddressAndDirection()) == true ) {
if (i2cPMU)
{
success = writePmuI2C(currentAddress, currentSubaddress, dataBuffer, (IOByteCount) nBytes );
return(success);
}
else
success = waitForCompletion();
}
}
else {
semaphore_create(current_task(), (semaphore**)&mySync, SYNC_POLICY_FIFO, 0);
myProvider->enableInterrupt(0);
if ((success = setAddressAndDirection()) == true ) {
timeout.tv_sec = 15; timeout.tv_nsec = 0;
if (0 != semaphore_timedwait( mySync, timeout )) {
success = false;
semaphore_destroy(current_task(), mySync); myProvider->disableInterrupt(0); IOLog("APPLE I2C WRITE SEMAPHORE EXCEEDED TIMEOUT OR OTHER ERROR\n");
}
else
{
semaphore_destroy(current_task(), mySync); myProvider->disableInterrupt(0); }
} else {
semaphore_destroy(current_task(), mySync);
myProvider->disableInterrupt(0);
}
if (!(transferWasSuccesful && success))
IOLog("APPLE I2C WRITE FAILED INTERRUPT TRANSFER, address = 0x%2x\n", address);
}
return (transferWasSuccesful && success);
}
bool
PPCI2CInterface::readI2CBus(UInt8 address, UInt8 subAddress, UInt8 *newData, UInt16 len)
{
mach_timespec_t timeout;
if (!ml_at_interrupt_context())
if (!IORecursiveLockHaveLock(mutexLock)) {
#if DEBUGMODE
IOLog("PPCI2CInterface::readI2CBus I am not the owner of the lock returns without doing anything.\n");
#endif return false;
}
bool success = false;
dataBuffer = newData;
nBytes = len;
currentAddress = address;
currentSubaddress = subAddress;
isReading = true;
if (pollingMode) {
if ((success = setAddressAndDirection()) == true ) {
if (i2cPMU)
{
success = readPmuI2C(currentAddress, currentSubaddress, dataBuffer, (IOByteCount) nBytes );
return(success);
}
else
success = waitForCompletion();
}
}
else {
semaphore_create(current_task(), (semaphore**)&mySync, SYNC_POLICY_FIFO, 0);
myProvider->enableInterrupt(0);
if ((success = setAddressAndDirection()) == true ) {
timeout.tv_sec = 15; timeout.tv_nsec = 0;
if (0 != semaphore_timedwait( mySync, timeout )) {
success = false;
semaphore_destroy(current_task(), mySync); myProvider->disableInterrupt(0); IOLog("APPLE I2C READ SEMAPHORE EXCEEDED TIMEOUT OR OTHER ERROR\n");
}
else
{
semaphore_destroy(current_task(), mySync); myProvider->disableInterrupt(0); }
} else {
semaphore_destroy(current_task(), mySync);
myProvider->disableInterrupt(0);
}
if (!(transferWasSuccesful && success))
IOLog("APPLE I2C READ FAILED INTERRUPT TRANSFER, address = 0x%2x\n", address);
}
return (transferWasSuccesful && success);
}
bool
PPCI2CInterface::closeI2CBus()
{
if (!IORecursiveLockHaveLock(mutexLock)) {
#if DEBUGMODE
IOLog("PPCI2CInterface::closeI2CBus I am not the owner of the lock returns without doing anything.\n");
#endif // DEBUGMODE
return false;
}
setPollingMode(true);
IORecursiveLockUnlock(mutexLock);
#if DEBUGMODE
IOLog("PPCI2CInterface::closeI2CBus PPCI2CInterface->mutexLock unlocked.\n");
#endif // DEBUGMODE
#ifdef DEBUGPMU
if (i2cPMU)
IOLog(" APPLE I2C-PMU CLOSED , SUCCESSFULLY! \n");
#endif // DEBUGPMU
return true;
}
const char *
PPCI2CInterface::getResourceName()
{
OSData *t;
char *dot = ".";
strcpy(resourceName, getName());
if (!i2cPMU)
t = OSDynamicCast(OSData, getProvider()->getProperty("AAPL,driver-name"));
else {
t = OSDynamicCast(OSData, getProvider()->getProperty("name"));
strncat(resourceName, (char*)dot, 1);
}
if (t != NULL)
strncat(resourceName, (char*)t->getBytesNoCopy(), t->getLength());
#ifdef DEBUGMODE
IOLog("PPCI2CInterface::getResourceName returns \"%s\"\n",resourceName);
#endif // DEBUGMODE
return (resourceName);
}
#define kWriteI2Cbus "writeI2CBus"
#define kReadI2Cbus "readI2CBus"
#define kOpenI2Cbus "openI2CBus"
#define kCloseI2Cbus "closeI2CBus"
#define kSetDumbMode "setDumbMode"
#define kSetPollingMode "setPollingMode"
#define kSetStandardMode "setStandardMode"
#define kSetStandardSubMode "setStandardSubMode"
#define kSetCombinedMode "setCombinedMode"
#define kRegisterForI2cInterrupts "registerForI2cInterrupts"
#define kDeRegisterI2cClient "deRegisterI2cClient"
IOReturn
PPCI2CInterface::callPlatformFunction( const OSSymbol *functionSymbol,
bool waitForFunction,
void *param1, void *param2,
void *param3, void *param4 ) {
const char *functionName = functionSymbol->getCStringNoCopy(); #ifdef DEBUGPMU
IOLog("APPLE I2C::CALL PLATFORM FUNCTION calling %s %s 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n",
functionName, (waitForFunction ? "TRUE" : "FALSE"),
param1, param2, param3, param4);
#endif // DEBUGPMU
if (strcmp(functionName, kWriteI2Cbus) == 0) {
if (writeI2CBus((UInt8)param1, (UInt8)param2, (UInt8 *)param3, (UInt16)param4))
return (kIOReturnSuccess);
else
return (kIOReturnBadArgument);
}
else if (strcmp(functionName, kReadI2Cbus) == 0) {
if (readI2CBus((UInt8)param1, (UInt8)param2, (UInt8 *)param3, (UInt16)param4))
return (kIOReturnSuccess);
else
return (kIOReturnBadArgument);
}
else if (strcmp(functionName, kOpenI2Cbus) == 0) {
if (openI2CBus((UInt8) param1)) return (kIOReturnSuccess);
else
return (kIOReturnBadArgument);
}
else if (strcmp(functionName, kCloseI2Cbus) == 0) {
if (closeI2CBus())
return (kIOReturnSuccess);
else
return (kIOReturnBadArgument);
}
else if (strcmp(functionName, kRegisterForI2cInterrupts) == 0) {
assert((param2 != 0) && (param3 != 0));
if (registerForI2cInterrupts((UInt32)param1, (AppleI2Cclient)param2, (IOService *) param3))
return (kIOReturnSuccess);
else
return (kIOReturnBadArgument);
}
else if (strcmp(functionName, kDeRegisterI2cClient) == 0) {
assert(param2 != 0);
if (deRegisterI2cClient((UInt32)param1, (IOService *)param2))
return (kIOReturnSuccess);
else
return (kIOReturnBadArgument);
}
else if (strcmp(functionName, kSetDumbMode) == 0) {
setDumbMode();
return (kIOReturnSuccess);
}
else if (strcmp(functionName, kSetStandardMode) == 0) {
setStandardMode();
return (kIOReturnSuccess);
}
else if (strcmp(functionName, kSetStandardSubMode) == 0) {
setStandardSubMode();
return (kIOReturnSuccess);
}
else if (strcmp(functionName, kSetCombinedMode) == 0) {
setCombinedMode();
return (kIOReturnSuccess);
}
else if (strcmp(functionName, kSetPollingMode) == 0) {
if (setPollingMode((bool) param1))
return (kIOReturnSuccess);
else
return (kIOReturnBadArgument);
}
return super::callPlatformFunction (functionSymbol, waitForFunction,
param1, param2, param3, param4);
}
IOReturn
PPCI2CInterface::callPlatformFunction( const char *functionName,
bool waitForFunction,
void *param1, void *param2,
void *param3, void *param4 )
{
return super::callPlatformFunction(functionName, waitForFunction,
param1, param2, param3, param4);
}
IOReturn
PPCI2CInterface::ApplePMUSendMiscCommand( UInt32 command,
IOByteCount sendLength, UInt8 * sendBuffer,
IOByteCount * readLength, UInt8 * readBuffer )
{
struct SendMiscCommandParameterBlock {
int command;
IOByteCount sLength;
UInt8 *sBuffer;
IOByteCount *rLength;
UInt8 *rBuffer;
};
IOReturn ret = kIOReturnError;
static IOService * pmu = 0;
if( !pmu)
pmu = IOService::waitForService(IOService::serviceMatching("ApplePMU"));
SendMiscCommandParameterBlock params = { command, sendLength, sendBuffer,
readLength, readBuffer };
if( pmu)
ret = pmu->callPlatformFunction( "sendMiscCommand", true,
(void*)¶ms, NULL, NULL, NULL );
return( ret );
}
bool
PPCI2CInterface::readPmuI2C( UInt8 address, UInt8 subAddress, UInt8 * readBuf, IOByteCount count )
{
IOReturn ioResult = kIOReturnError;
PMUI2CPB iicPB;
IOByteCount readLen;
UInt32 retries; bool success;
UInt8 myreadbuffer[256];
IOByteCount length;
success = false; retries = MAXIICRETRYCOUNT;
while (retries--) {
iicPB.bus = portSelect;
iicPB.xferType = lastMode; iicPB.secondaryBusNum = 0; iicPB.address = address;
iicPB.subAddr = subAddress; iicPB.combAddr = address; iicPB.dataCount = count; if (lastMode == kCombinedI2CStream)
iicPB.address &= 0xFE; readLen = count; length = (short) ((UInt8 *)&iicPB.data - (UInt8 *)&iicPB.bus);
ioResult = ApplePMUSendMiscCommand( kPMUI2CCmd, length, (UInt8 *) &iicPB, &readLen, myreadbuffer );
if ((ioResult == kIOReturnSuccess) && (myreadbuffer[0] == STATUS_OK))
break;
IOSleep( 15 );
}
#ifdef DEBUGPMU
IOLog("READ PMU MID STATUS, retries = 0x%02x\n", MAXIICRETRYCOUNT - retries);
#endif // DEBUGPMU
if (myreadbuffer[0] == STATUS_OK)
{
ioResult = kIOReturnError;
success = false; retries = MAXIICRETRYCOUNT;
while (retries--)
{
IOSleep( 15 );
iicPB.bus = kI2CStatusBus;
readLen = count+1; myreadbuffer[0] = 0xff;
ioResult = ApplePMUSendMiscCommand( kPMUI2CCmd, 1, (UInt8 *) &iicPB, &readLen, myreadbuffer );
if ( (ioResult == kIOReturnSuccess) && ( ( myreadbuffer[0] == STATUS_DATAREAD ) || ((SInt8)myreadbuffer[0] >= STATUS_OK ) ) )
{
if ((SInt8)myreadbuffer[0] >= STATUS_DATAREAD )
{
bcopy( 1 + myreadbuffer, readBuf, readLen-1 ); success = true;
};
#ifdef DEBUGPMU
IOLog("READ I2C PMU END - STATUS OK: retries = 0x%02x\n", MAXIICRETRYCOUNT - retries);
IOLog("addr = 0x%02x, subAd = 0x%02x, rdBuf = 0x%02x, count = 0x%ld)\n", address, subAddress, myreadbuffer[0], count);
#endif // DEBUGPMU
break;
}
}
}
return(success);
}
bool
PPCI2CInterface::writePmuI2C( UInt8 address, UInt8 subAddress, UInt8 * buffer, IOByteCount count )
{
IOReturn ioResult = kIOReturnError;
PMUI2CPB iicPB;
IOByteCount readLen;
UInt8 readBuf[8];
UInt32 retries; bool success;
IOByteCount length;
#ifdef DEBUGPMU
IOLog("WRITE PMU START\n");
IOLog("addr = 0x%02x, subAd = 0x%02x, wrBuf = 0x%02x, count = 0x%ld)\n", address, subAddress, buffer[0], count);
#endif // DEBUGPMU
ioResult = kIOReturnError;
success = false; retries = MAXIICRETRYCOUNT;
while (retries--) {
UInt8 cnt;
iicPB.bus = portSelect;
iicPB.xferType = lastMode; iicPB.secondaryBusNum = 0; iicPB.address = address;
iicPB.subAddr = subAddress; iicPB.combAddr = address; iicPB.dataCount = count; if (lastMode == kCombinedI2CStream)
iicPB.address &= 0xFE;
for (cnt = 0; cnt < count; cnt++) iicPB.data[cnt] = buffer[cnt];
readLen = 1; readBuf[0] = 0xff;
length = (short) (&iicPB.data[iicPB.dataCount] - &iicPB.bus);
ioResult = ApplePMUSendMiscCommand( kPMUI2CCmd, length, (UInt8 *) &iicPB, &readLen, readBuf );
if ( (ioResult == kIOReturnSuccess) && (readBuf[0] == STATUS_OK) )
break;
IOSleep( 15 );
}
#ifdef DEBUGPMU
IOLog("WRITE PMU MID STATUS, retries = 0x%02x\n", MAXIICRETRYCOUNT - retries);
#endif // DEBUGPMU
if (readBuf[0] == STATUS_OK) {
ioResult = kIOReturnError;
success = false; retries = MAXIICRETRYCOUNT;
while (retries--) {
IOSleep( 15 );
iicPB.bus = kI2CStatusBus;
readLen = sizeof( readBuf );
readBuf[0] = 0xff;
ioResult = ApplePMUSendMiscCommand( kPMUI2CCmd, 1, (UInt8 *) &iicPB, &readLen, readBuf );
if ( (ioResult == kIOReturnSuccess) && (readBuf[0] == STATUS_OK) ) {
#ifdef DEBUGPMU
IOLog("WRITE PMU STATUS OK, retries = 0x%02x\n", MAXIICRETRYCOUNT - retries);
#endif // DEBUGPMU
success = true;
break;
}
}
}
return(success);
}
bool PPCI2CInterface::addI2Cclient(UInt32 addressInfo, AppleI2Cclient function, IOService * caller)
{
bool result = false;
if (clientMutexLock != NULL)
IOLockLock(clientMutexLock);
I2CclientPtr newClient = (I2CclientPtr)IOMalloc(sizeof(I2Cclient));
if (newClient != NULL) {
newClient->nextClient = listHead;
newClient->addressInfo = addressInfo;
newClient->client = caller;
newClient->callBackFunction = function;
listHead = newClient;
#ifdef DEBUGPMU
IOLog("APPLEI2C::addI2Cclient - NEW INTERRUPT CLIENT ADDED\n");
char debugInfo[128];
sprintf(debugInfo, "APPLE_PMU ADD CLIENT: interruptMask = 0x01\n");
setProperty("PMU_AddClient", debugInfo);
IOLog ("APPLEI2C::addI2Cclient - NEW INTERRUPT CLIENT ADDED\n");
#endif // DEBUGPMU
result = true;
}
if (clientMutexLock != NULL)
IOLockUnlock(clientMutexLock);
return result;
}
bool
PPCI2CInterface::removeI2Cclient(UInt32 addressInfo, IOService * caller)
{
if (clientMutexLock != NULL)
IOLockLock(clientMutexLock);
I2CclientPtr tmp, tmpPrev = NULL;
for (tmp = listHead; tmp != NULL; tmp = tmp->nextClient) {
if ((tmp->client == caller) && (tmp->addressInfo == addressInfo))
{
if (tmp == listHead) {
listHead = tmp->nextClient;
}
else if (tmpPrev !=NULL) {
tmpPrev->nextClient = tmp->nextClient;
}
else {
#ifdef DEBUGPMU
IOLog("PPCI2CInterface:removeI2Cclient, REMOVED INTERRUPT CLIENT\n");
#endif // DEBUGPMU
return false;
}
IOFree(tmp, sizeof(I2Cclient));
if (clientMutexLock != NULL)
IOLockUnlock(clientMutexLock);
return true;
}
else {
tmpPrev = tmp;
}
}
if (clientMutexLock != NULL)
IOLockUnlock(clientMutexLock);
return false;
}
bool
PPCI2CInterface::clearI2CclientList()
{
bool success = true;
while (listHead != NULL) {
success &= removeI2Cclient(listHead->addressInfo, listHead->client);
}
return success;
}
bool
PPCI2CInterface::calli2cClient(UInt8 interruptMask, UInt32 length, UInt8 * buffer)
{
#define kMaxClients 64
I2CclientPtr clientToCall, clientsCalled[kMaxClients];
long clientCalledCount, index;
bool success = false;
UInt8 mybuffer[256];
clientCalledCount = 0;
UInt8 myTypeInfo = buffer[0];
if ( !( (interruptMask == 0x01) && (myTypeInfo == 0x00) ) ) {
return (false);
}
UInt32 myAddrInfo = (((((UInt32) buffer[1]) << 16 ) & 0xFF0000) | ((((UInt32) buffer[2]) << 8 ) & 0xFF00) | ((((UInt32) buffer[3]) << 0 ) & 0xFF));
do {
if (clientMutexLock != NULL)
IOLockLock(clientMutexLock);
for (clientToCall = listHead; clientToCall != NULL; clientToCall = clientToCall->nextClient)
{
#ifdef DEBUGPMU
#endif // DEBUGPMU
if (clientToCall->addressInfo == myAddrInfo)
{
for (index = 0; index < clientCalledCount; index++)
if (clientToCall == clientsCalled[index])
break;
if (index == clientCalledCount)
break;
}
}
if (clientMutexLock != NULL)
IOLockUnlock(clientMutexLock);
if (clientToCall != NULL) {
success = true;
bcopy( 4 + buffer, mybuffer, length-4 ); #ifdef DEBUGPMU
IOLog ("APPLEI2C::callI2Cclient - calling back client.........\n");
#endif // DEBUGPMU
(*clientToCall->callBackFunction)(clientToCall->client, clientToCall->addressInfo, length-4, mybuffer);
clientsCalled[clientCalledCount++] = clientToCall;
if (clientCalledCount >= kMaxClients) {
IOLog ("PPCI2CInterface::callI2Cclient - too many clients to call\n");
break;
}
}
} while (clientToCall != NULL);
return (success);
}
bool
PPCI2CInterface::registerForI2cInterrupts(UInt32 addressInfo, AppleI2Cclient function, IOService * caller)
{
IOReturn ret = kIOReturnError;
static IOService * pmu;
if ( !registeredForPmuI2C && !pmu)
{
#ifdef DEBUGPMU
IOLog ("APPLEI2C::RegisterI2Cclient1\n");
#endif // DEBUGPMU
pmu = IOService::waitForService(IOService::serviceMatching("ApplePMU"));
}
if ( !registeredForPmuI2C && pmu )
{
#ifdef DEBUGPMU
IOLog ("APPLEI2C::RegisterI2Cclient2\n");
#endif // DEBUGPMU
ret = pmu->callPlatformFunction( "registerForPMUInterrupts", true, (void*)0x01, (void*)handlePMUi2cInterrupt, (void*)this, NULL );
}
if ( (ret==kIOReturnSuccess) || registeredForPmuI2C )
{
#ifdef DEBUGPMU
IOLog ("APPLEI2C::RegisterI2Cclient3 - clientAddressInfo = 0x%08lx\n", addressInfo);
#endif // DEBUGPMU
registeredForPmuI2C = true; return (addI2Cclient(addressInfo, function, caller));
}
else
{
#ifdef DEBUGPMU
IOLog("APPLEI2C::RegisterI2Cclient FAILED to register for interrupts !!\n");
#endif // DEBUGPMU
return( false );
}
}
bool
PPCI2CInterface::deRegisterI2cClient(UInt32 addressInfo, IOService * caller)
{
return removeI2Cclient(addressInfo, caller);
}
void
PPCI2CInterface::handlePMUi2cInterrupt(IOService *client, UInt8 interruptMask, UInt32 length, UInt8 *buffer)
{
PPCI2CInterface *i2c = OSDynamicCast(PPCI2CInterface, client);
if (i2c != NULL)
i2c->calli2cClient(interruptMask, length, buffer); }