IOI2CControllerPMU.cpp [plain text]
#include <IOKit/pwr_mgt/RootDomain.h>
#include <IOKit/pwr_mgt/IOPMPrivate.h>
#include <IOKit/IOMessage.h>
#include <IOKit/IOLocks.h>
#include "IOI2CController.h"
#include "IOI2CDefs.h"
#if (defined(PMUI2C_DEBUG) && PMUI2C_DEBUG)
#define DLOG(fmt, args...) kprintf(fmt, ## args)
#else
#define DLOG(fmt, args...)
#endif
#define ERRLOG(fmt, args...) IOLog(fmt, ## args)
class IOI2CControllerPMU : public IOI2CController
{
OSDeclareDefaultStructors(IOI2CControllerPMU)
public:
virtual bool start(IOService *provider);
virtual void free ( void );
protected:
IOReturn processLockI2CBus(
UInt32 bus);
IOReturn processUnlockI2CBus(
UInt32 bus);
IOReturn processReadI2CBus(
IOI2CCommand *cmd);
IOReturn processWriteI2CBus(
IOI2CCommand *cmd);
private:
IOReturn ApplePMUSendI2CCommand(
UInt32 command,
IOByteCount sendLength,
UInt8 *sendBuffer,
IOByteCount *readLength,
UInt8 *readBuffer);
#define kPMUSleepDelay 15
enum
{
kPMUI2CCmd = 0x9A,
kI2CReplyPendingErr = -4,
kI2CTransactionErr = -3,
kI2CBusyErr = -2,
kI2CParameterErr = -1,
kI2CNoErr = 0,
kI2CReadData = 1,
kI2CDataBufSize = 249
};
enum
{
kPMUSimpleI2CStream = 0, kPMUSubaddressI2CStream = 1,
kPMUCombinedI2CStream = 2
};
typedef struct
{
UInt8 bus;
UInt8 xferType;
UInt8 secondaryBusNum;
UInt8 address;
UInt8 subAddr;
UInt8 combAddr;
UInt8 dataCount;
UInt8 data[249];
} PMUI2CPB;
typedef struct
{
UInt32 command;
IOByteCount sLength;
UInt8 *sBuffer;
IOByteCount *rLength;
UInt8 *rBuffer;
} SendMiscCommandParameterBlock;
enum
{
kI2CStatusBus = 0,
kSystemClockBus = 1,
kPowerSupplyBus = 2,
};
#define MAXIICRETRYCOUNT 20
#define STATUS_DATAREAD 1
#define STATUS_OK 0
#define STATUS_BUSY 0xfe
IOService *fApplePMU;
const OSSymbol *i2cCommandSym;
};
#define super IOI2CController
OSDefineMetaClassAndStructors( IOI2CControllerPMU, IOI2CController )
bool
IOI2CControllerPMU::start(IOService *provider)
{
mach_timespec_t timeout;
timeout.tv_sec = 30;
timeout.tv_nsec = 0;
DLOG("+IOI2CControllerPMU::start\n");
fApplePMU = waitForService(serviceMatching("ApplePMU"), &timeout);
if (NULL == fApplePMU)
{
ERRLOG("IOI2CControllerPMU::start timeout waiting for ApplePMU\n");
return false;
}
i2cCommandSym = OSSymbol::withCStringNoCopy("sendMiscCommand");
if (false == super::start(provider))
return false;
registerService();
publishChildren();
DLOG("-IOI2CControllerPMU::start\n");
return true;
}
void IOI2CControllerPMU::free ( void )
{
if (i2cCommandSym) { i2cCommandSym->release(); i2cCommandSym = 0; }
super::free();
}
IOReturn
IOI2CControllerPMU::processLockI2CBus(
UInt32 bus)
{
DLOG("\n+IOI2CControllerPMU::processLockI2CBus B:%x\n", bus);
return kIOReturnSuccess;
}
IOReturn
IOI2CControllerPMU::processUnlockI2CBus(
UInt32 bus)
{
DLOG("\n+IOI2CControllerPMU::processUnlockI2CBus B:%x\n", bus);
return kIOReturnSuccess;
}
#define kMAX_READ_LENGTH 256
IOReturn
IOI2CControllerPMU::processReadI2CBus(
IOI2CCommand *cmd)
{
IOReturn status = kIOReturnError;
UInt32 bus;
UInt32 address;
UInt32 subAddress;
UInt8 *buffer;
UInt32 count;
UInt32 mode;
PMUI2CPB iicPB = {0};
IOByteCount rLength;
SInt32 retries; UInt8 rBuffer[kMAX_READ_LENGTH] = {0};
IOByteCount sLength;
UInt32 subAddr;
UInt8 xferType;
UInt8 combAddr;
if (cmd == NULL)
return kIOReturnBadArgument;
bus = cmd->bus;
address = cmd->address;
combAddr = address;
subAddress = cmd->subAddress;
buffer = cmd->buffer;
count = cmd->count;
mode = cmd->mode;
cmd->bytesTransfered = 0;
DLOG("\n+IOI2CControllerPMU::processReadI2CBus B:%x A:%x S:%x L:%x M:%x\n",
bus, address, subAddress, count, mode);
if (count > fMaxI2CDataLength)
{
DLOG("-IOI2CControllerPMU::processReadI2CBus max supported byte count exceeded\n");
return kIOReturnBadArgument;
}
if (isI2C10BitAddress(address))
{
DLOG("-IOI2CControllerPMU::processReadI2CBus 10-bit addressing not supported\n");
return kIOReturnBadArgument;
}
switch (mode) {
case kI2CMode_Unspecified:
case kI2CMode_Standard:
xferType = kPMUSimpleI2CStream;
subAddr = 0;
break;
case kI2CMode_StandardSub:
case kI2CMode_Combined:
if (mode == kI2CMode_StandardSub)
xferType = kPMUSubaddressI2CStream;
else
{
combAddr |= 1;
xferType = kPMUCombinedI2CStream;
}
switch (subAddress >> 24)
{
case 1:
case 0: subAddr = (subAddress & 0x000000ff); break;
default:
status = kIOReturnBadArgument;
DLOG("-IOI2CControllerPMU::processReadI2CBus unsupported subAddress <0x%08x> format error:%x\n", subAddress, status);
return status;
}
break;
default:
status = kIOReturnBadArgument;
DLOG("-IOI2CControllerPMU::processReadI2CBus unsupported mode error:%x\n", status);
return status;
}
for (retries = 0; retries < MAXIICRETRYCOUNT; retries++)
{
iicPB.bus = bus;
iicPB.xferType = xferType;
iicPB.secondaryBusNum = 0; iicPB.address = address;
iicPB.subAddr = subAddr;
iicPB.combAddr = combAddr; iicPB.dataCount = count; rLength = count; sLength = (short) ((UInt8 *)&iicPB.data - (UInt8 *)&iicPB.bus);
DLOG("PMUI2C send length=%d\n",sLength);
#ifdef PMUI2C_DEBUG
{
UInt32 iii;
UInt8 *sBuffer = (UInt8 *) &iicPB;
DLOG("PMUI2C:read1 sL:%d, rL:%d, data:", sLength, rLength);
for (iii = 0; iii < sLength; iii++)
DLOG(" %02x", sBuffer[iii]);
DLOG("\n");
}
#endif
if (kIOReturnSuccess == (status = ApplePMUSendI2CCommand( kPMUI2CCmd, sLength, (UInt8 *) &iicPB, &rLength, rBuffer )))
{
if (rBuffer[0] == STATUS_OK)
break; }
IOSleep (15); }
DLOG("READ PMU MID STATUS:0x%x, retries = %d\n", status, retries);
if (retries >= MAXIICRETRYCOUNT)
status = kIOReturnNotResponding;
if (status == kIOReturnSuccess)
{
IOSleep (kPMUSleepDelay);
for (retries = 0; retries < MAXIICRETRYCOUNT; retries++)
{
iicPB.bus = kI2CStatusBus;
rLength = count + 1; rBuffer[0] = 0xff;
sLength = 1;
if (kIOReturnSuccess == (status = ApplePMUSendI2CCommand( kPMUI2CCmd, sLength, (UInt8 *) &iicPB, &rLength, rBuffer )))
{
#ifdef PMUI2C_DEBUG
{
UInt32 iii;
DLOG("PMUI2C:read3 sL:%d, rL:%d, data:", sLength, rLength);
for (iii = 0; iii < rLength; iii++)
DLOG(" %02x", rBuffer[iii]);
DLOG("\n");
}
#endif
if ((SInt8)rBuffer[0] <= (SInt8)STATUS_BUSY)
{
DLOG("PMUI2C:busy retries:%d\n", retries);
status = kIOReturnBusy;
}
else
if ((SInt8)rBuffer[0] >= STATUS_OK)
{
if ((SInt8)rBuffer[0] >= STATUS_DATAREAD)
{
DLOG("PMUI2C:rLength:%d, count:%d\n", rLength, count);
bcopy(1 + rBuffer, buffer, count);
cmd->bytesTransfered = count;
status = kIOReturnSuccess;
break;
}
DLOG("PMUI2C:read rBuffer[0] == STATUS_OK (%x) considering this an error:%x!!!\n", rBuffer[0], status);
status = kIOReturnIOError;
#ifdef PMUI2C_DEBUG
{
UInt32 iii;
DLOG("PMUI2C:read4 sL:%d, rL:%d, data:", sLength, rLength);
for (iii = 0; iii < rLength; iii++)
DLOG(" %02x", rBuffer[iii]);
DLOG("\n");
}
#endif
break;
}
}
IOSleep( 15 );
}
}
DLOG("READ I2C PMU END - STATUS:0x%x retries = %d\n", status, retries);
DLOG("addr = 0x%02lx, subAd = 0x%02lx, rdBuf[0] = 0x%02x, count = 0x%ld\n", address, subAddress, rBuffer[0], count);
DLOG("-IOI2CControllerPMU::processReadI2CBus\n\n");
return status;
}
IOReturn
IOI2CControllerPMU::processWriteI2CBus(
IOI2CCommand *cmd)
{
IOReturn status = kIOReturnError;
UInt32 bus;
UInt32 address;
UInt32 subAddress;
UInt8 *buffer;
UInt32 count;
UInt32 mode;
PMUI2CPB iicPB = {0};
IOByteCount rLength;
UInt8 rBuffer[8] = {0};
SInt32 retries; IOByteCount sLength;
UInt32 subAddr;
UInt8 xferType;
UInt8 combAddr;
if (cmd == NULL)
return kIOReturnBadArgument;
bus = cmd->bus;
address = cmd->address;
combAddr = address;
subAddress = cmd->subAddress;
buffer = cmd->buffer;
count = cmd->count;
mode = cmd->mode;
cmd->bytesTransfered = 0;
DLOG("\n+IOI2CControllerPMU::processWriteI2CBus B:%x A:%x S:%x L:%x M:%x\n",
bus, address, subAddress, count, mode);
if (count > fMaxI2CDataLength)
{
DLOG("-IOI2CControllerPMU::processReadI2CBus max supported byte count exceeded\n");
return kIOReturnBadArgument;
}
if (isI2C10BitAddress(address))
{
DLOG("-IOI2CControllerPMU::processWriteI2CBus 10-bit addressing not supported\n");
return kIOReturnBadArgument;
}
switch (mode) {
case kI2CMode_Unspecified:
case kI2CMode_Standard:
subAddr = 0;
xferType = kPMUSimpleI2CStream;
break;
case kI2CMode_StandardSub:
case kI2CMode_Combined:
if (mode == kI2CMode_StandardSub)
xferType = kPMUSubaddressI2CStream;
else
{
combAddr &= ~1;
xferType = kPMUCombinedI2CStream;
}
switch (subAddress >> 24)
{
case 1:
case 0: subAddr = (subAddress & 0x000000ff); break;
default:
status = kIOReturnBadArgument;
DLOG("-IOI2CControllerPMU::processWriteI2CBus unsupported subAddress <0x%08x> format error:%x\n", subAddress, status);
return status;
}
break;
default:
status = kIOReturnBadArgument;
DLOG("-IOI2CControllerPMU::processWriteI2CBus unsupported mode error:%x\n", status);
return status;
}
DLOG("\n+IOI2CControllerPMU::processWriteI2CBus\nA:0x%02lx, S:0x%02lx, wrBuf:0x%02x, L:%ld\n",
address, subAddress, buffer[0], count);
for (retries = 0; retries < MAXIICRETRYCOUNT; retries++)
{
iicPB.bus = bus;
iicPB.xferType = xferType;
iicPB.secondaryBusNum = 0; iicPB.address = address;
iicPB.subAddr = subAddr;
iicPB.combAddr = combAddr; iicPB.dataCount = count;
bcopy(buffer, iicPB.data, count);
rLength = 1; rBuffer[0] = 0xff;
sLength = (short) (&iicPB.data[count] - &iicPB.bus);
#ifdef PMUI2C_DEBUG
{
UInt32 iii;
UInt8 *sBuffer = (UInt8 *)&iicPB;
DLOG("PMUI2C:write1 sL:%d, rL:%d, data:", sLength, rLength);
for (iii = 0; iii < sLength; iii++)
DLOG(" %02x", sBuffer[iii]);
DLOG("\n");
}
#endif
if (kIOReturnSuccess == (status = ApplePMUSendI2CCommand( kPMUI2CCmd, sLength, (UInt8 *) &iicPB, &rLength, rBuffer )))
{
#ifdef PMUI2C_DEBUG
{
UInt32 iii;
DLOG("PMUI2C:write2 sL:%d, rL:%d, data:", sLength, rLength);
for (iii = 0; iii < rLength; iii++)
DLOG(" %02x", rBuffer[iii]);
DLOG("\n");
}
#endif
if (rBuffer[0] == STATUS_OK)
break; else
status = kIOReturnIOError;
}
IOSleep(15);
}
DLOG("WRITE PMU MID STATUS:%x, retries:%d\n", status, retries);
if (retries >= MAXIICRETRYCOUNT)
status = kIOReturnNotResponding;
if (status == kIOReturnSuccess)
{
IOSleep (kPMUSleepDelay);
for (retries = 0; retries < MAXIICRETRYCOUNT; retries++)
{
iicPB.bus = kI2CStatusBus; rLength = sizeof( rBuffer );
rBuffer[0] = 0xff;
sLength = 1;
#ifdef PMUI2C_DEBUG
{
UInt32 iii;
UInt8 *sBuffer = (UInt8 *)&iicPB;
DLOG("PMUI2C:write3 sL:%d, rL:%d, data:", sLength, rLength);
for (iii = 0; iii < sLength; iii++)
DLOG(" %02x", sBuffer[iii]);
DLOG("\n");
}
#endif
if (kIOReturnSuccess == (status = ApplePMUSendI2CCommand( kPMUI2CCmd, sLength, (UInt8 *) &iicPB, &rLength, rBuffer )))
{
#ifdef PMUI2C_DEBUG
{
UInt32 iii;
DLOG("PMUI2C:write4 sL:%d, rL:%d, data:", sLength, rLength);
for (iii = 0; iii < rLength; iii++)
DLOG(" %02x", rBuffer[iii]);
DLOG("\n");
}
#endif
if (rBuffer[0] == STATUS_OK)
{
cmd->bytesTransfered = count;
break;
}
status = kIOReturnIOError;
DLOG("WRITE PMU STATUS:0x%x, retries:%02lx\n", status, retries);
}
IOSleep(15);
}
}
DLOG("-IOI2CControllerPMU::processWriteI2CBus\n\n");
return status;
}
IOReturn
IOI2CControllerPMU::ApplePMUSendI2CCommand(
UInt32 command,
IOByteCount sendLength,
UInt8 *sendBuffer,
IOByteCount *readLength,
UInt8 *readBuffer)
{
IOReturn ret;
SendMiscCommandParameterBlock params = { command, sendLength, sendBuffer, readLength, readBuffer };
ret = fApplePMU->callPlatformFunction( "sendMiscCommand", true, (void*)¶ms, NULL, NULL, NULL );
return ret;
}