IOI2CControllerSMU.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(SMUI2C_DEBUG) && SMUI2C_DEBUG)
#define DLOG(fmt, args...) kprintf(fmt, ## args)
#else
#define DLOG(fmt, args...)
#endif
class IOI2CControllerSMU : public IOI2CController
{
OSDeclareDefaultStructors(IOI2CControllerSMU)
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 AppleSMUSendI2CCommand(
IOByteCount sendLength,
UInt8 *sendBuffer,
IOByteCount *readLength,
UInt8 *readBuffer,
UInt8 command);
enum
{
kSMUSleepDelay = 150 };
enum
{
kSMU_Open = 1,
kSMU_I2C_Cmd = 2,
kSMU_Close = 3
};
enum
{
kSimpleI2CStream = 0, kSubaddressI2CStream = 1,
kCombinedI2CStream = 2
};
typedef struct
{
UInt8 bus;
UInt8 xferType;
UInt8 address;
UInt8 subAddr[4];
UInt8 combAddr;
UInt8 dataCount;
UInt8 data[246];
} SMUI2CPB;
enum
{
kI2CStatusBus = 0,
kSystemClockBus = 1,
kPowerSupplyBus = 2,
};
#define MAXIICRETRYCOUNT 20
#define STATUS_DATAREAD 1
#define STATUS_OK 0
#define STATUS_BUSY 0xfe
UInt16 waitTime;
const OSSymbol *i2cCommandSym;
const OSSymbol *i2cOpenSym;
const OSSymbol *i2cCloseSym;
};
#define super IOI2CController
OSDefineMetaClassAndStructors( IOI2CControllerSMU, IOI2CController )
bool
IOI2CControllerSMU::start(IOService *provider)
{
DLOG("+IOI2CControllerSMU::start\n");
if (false == super::start(provider))
return false;
i2cCommandSym = OSSymbol::withCStringNoCopy("sendSMUI2CCommand");
i2cOpenSym = OSSymbol::withCStringNoCopy("openSMUI2CCommand");
i2cCloseSym = OSSymbol::withCStringNoCopy("closeSMUI2CCommand");
registerService();
publishChildren();
DLOG("-IOI2CControllerSMU::start\n");
return true;
}
void IOI2CControllerSMU::free ( void )
{
if (i2cCommandSym) { i2cCommandSym->release(); i2cCommandSym = 0; }
if (i2cOpenSym) { i2cOpenSym->release(); i2cOpenSym = 0; }
if (i2cCloseSym) { i2cCloseSym->release(); i2cCloseSym = 0; }
super::free();
}
IOReturn
IOI2CControllerSMU::processLockI2CBus(
UInt32 bus)
{
IOReturn status;
status = AppleSMUSendI2CCommand( (IOByteCount)bus, NULL, NULL, NULL, kSMU_Open);
return status;
}
IOReturn
IOI2CControllerSMU::processUnlockI2CBus(
UInt32 bus)
{
IOReturn status;
status = AppleSMUSendI2CCommand( (IOByteCount)bus, NULL, NULL, NULL, kSMU_Close);
return status;
}
#define kMAX_READ_LENGTH 256
IOReturn
IOI2CControllerSMU::processReadI2CBus(
IOI2CCommand *cmd)
{
IOReturn status = kIOReturnError;
UInt32 bus;
UInt32 address;
UInt32 subAddress;
UInt8 *buffer;
UInt32 count;
UInt32 mode;
SMUI2CPB iicPB = {0};
IOByteCount rLength;
SInt32 retries; UInt8 rBuffer[kMAX_READ_LENGTH] = {0};
IOByteCount sLength;
UInt32 smuSubAddr;
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+IOI2CControllerSMU::processReadI2CBus\n");
if (count > fMaxI2CDataLength)
{
DLOG("-IOI2CControllerSMU::processReadI2CBus max supported byte count exceeded\n");
return kIOReturnBadArgument;
}
if (isI2C10BitAddress(address))
{
DLOG("-IOI2CControllerSMU::processReadI2CBus 10-bit addressing not supported\n");
return kIOReturnBadArgument;
}
switch (mode) {
case kI2CMode_Unspecified:
case kI2CMode_Standard:
xferType = kSimpleI2CStream;
smuSubAddr = 0;
break;
case kI2CMode_StandardSub:
case kI2CMode_Combined:
if (mode == kI2CMode_StandardSub)
xferType = kSubaddressI2CStream;
else
{
combAddr |= 1;
xferType = kCombinedI2CStream;
}
switch (subAddress >> 24)
{
case 3: smuSubAddr = subAddress; break;
case 2: smuSubAddr = (2 << 24) | ((subAddress & 0x0000ffff) << 8); break;
case 1:
case 0: smuSubAddr = (1 << 24) | ((subAddress & 0x000000ff) << 16); break;
default:
status = kIOReturnBadArgument;
DLOG("-IOI2CControllerSMU::processReadI2CBus unsupported subAddress <0x%08x> format error:%x\n", subAddress, status);
return status;
}
break;
default:
status = kIOReturnBadArgument;
DLOG("-IOI2CControllerSMU::processReadI2CBus unsupported mode error:%x\n", status);
return status;
}
for (retries = 0; retries < MAXIICRETRYCOUNT; retries++)
{
iicPB.bus = bus;
iicPB.xferType = xferType;
iicPB.address = address;
iicPB.subAddr[0] = ((UInt8*)&smuSubAddr)[0];
iicPB.subAddr[1] = ((UInt8*)&smuSubAddr)[1];
iicPB.subAddr[2] = ((UInt8*)&smuSubAddr)[2];
iicPB.subAddr[3] = ((UInt8*)&smuSubAddr)[3];
iicPB.combAddr = combAddr; iicPB.dataCount = count; rLength = count; sLength = (short) ((UInt8 *)&iicPB.data - (UInt8 *)&iicPB.bus);
DLOG("SMUI2C send length=%d\n",sLength);
#ifdef SMUI2C_DEBUG
{
UInt32 iii;
UInt8 *sBuffer = (UInt8 *) &iicPB;
DLOG("SMUI2C:read1 sL:%d, rL:%d, data:", sLength, rLength);
for (iii = 0; iii < sLength; iii++)
DLOG(" %02x", sBuffer[iii]);
DLOG("\n");
}
#endif
if (kIOReturnSuccess == (status = AppleSMUSendI2CCommand( sLength, (UInt8 *) &iicPB, &rLength, rBuffer, kSMU_I2C_Cmd )))
{
#ifdef SMUI2C_DEBUG
{
UInt32 iii;
DLOG("SMUI2C:read2 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; }
IOSleep (15); }
DLOG("READ SMU MID STATUS:0x%x, retries = %d\n", status, retries);
if (retries >= MAXIICRETRYCOUNT)
status = kIOReturnNotResponding;
if (status == kIOReturnSuccess)
{
IOSleep (kSMUSleepDelay);
for (retries = 0; retries < MAXIICRETRYCOUNT; retries++)
{
iicPB.bus = kI2CStatusBus;
rLength = count + 1; rBuffer[0] = 0xff;
sLength = 1;
#ifdef SMUI2C_DEBUG
{
UInt32 iii;
UInt8 *sBuffer = (UInt8 *) &iicPB;
DLOG("SMUI2C:read3 sL:%d, rL:%d, data:", sLength, rLength);
for (iii = 0; iii < sLength; iii++)
DLOG(" %02x", sBuffer[iii]);
DLOG("\n");
}
#endif
if (kIOReturnSuccess == (status = AppleSMUSendI2CCommand( sLength, (UInt8 *) &iicPB, &rLength, rBuffer, kSMU_I2C_Cmd )))
{
#ifdef SMUI2C_DEBUG
{
UInt32 iii;
DLOG("SMUI2C:read4 sL:%d, rL:%d, data:", sLength, rLength);
for (iii = 0; iii < rLength; iii++)
DLOG(" %02x", rBuffer[iii]);
DLOG("\n");
}
#endif
if ((SInt8)rBuffer[0] >= STATUS_OK)
{
DLOG("SMUI2C:rLength:%d, count:%d\n", rLength, count);
bcopy( 1 + rBuffer, buffer, count ); }
else
{
status = kIOReturnIOError;
DLOG("SMUI2C:read rBuffer[0] < STATUS_OK (%x) considering this an error:%x!!!\n", rBuffer[0], status);
}
DLOG("READ I2C SMU 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);
cmd->bytesTransfered = count;
break;
}
IOSleep( 15 );
}
}
DLOG("-IOI2CControllerSMU::processReadI2CBus\n\n");
return status;
}
IOReturn
IOI2CControllerSMU::processWriteI2CBus(
IOI2CCommand *cmd)
{
IOReturn status = kIOReturnError;
UInt32 bus;
UInt32 address;
UInt32 subAddress;
UInt8 *buffer;
UInt32 count;
UInt32 mode;
SMUI2CPB iicPB = {0};
IOByteCount rLength;
UInt8 rBuffer[8] = {0};
SInt32 retries; IOByteCount sLength;
UInt32 smuSubAddr;
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;
if (count > fMaxI2CDataLength)
{
DLOG("-IOI2CControllerSMU::processReadI2CBus max supported byte count exceeded\n");
return kIOReturnBadArgument;
}
if (isI2C10BitAddress(address))
{
DLOG("-IOI2CControllerSMU::processWriteI2CBus 10-bit addressing not supported\n");
return kIOReturnBadArgument;
}
switch (mode) {
case kI2CMode_Unspecified:
case kI2CMode_Standard:
smuSubAddr = 0;
xferType = kSimpleI2CStream;
break;
case kI2CMode_StandardSub:
case kI2CMode_Combined:
if (mode == kI2CMode_StandardSub)
xferType = kSubaddressI2CStream;
else
{
combAddr |= 1;
xferType = kCombinedI2CStream;
}
switch (subAddress >> 24)
{
case 3: smuSubAddr = subAddress; break;
case 2: smuSubAddr = (2 << 24) | ((subAddress & 0x0000ffff) << 8); break;
case 1:
case 0: smuSubAddr = (1 << 24) | ((subAddress & 0x000000ff) << 16); break;
default:
status = kIOReturnBadArgument;
DLOG("-IOI2CControllerSMU::processReadI2CBus unsupported subAddress <0x%08x> format error:%x\n", subAddress, status);
return status;
}
break;
default:
status = kIOReturnBadArgument;
DLOG("-IOI2CControllerSMU::processReadI2CBus unsupported mode error:%x\n", status);
return status;
}
DLOG("\n+IOI2CControllerSMU::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.address = address;
iicPB.subAddr[0] = ((UInt8*)&smuSubAddr)[0];
iicPB.subAddr[1] = ((UInt8*)&smuSubAddr)[1];
iicPB.subAddr[2] = ((UInt8*)&smuSubAddr)[2];
iicPB.subAddr[3] = ((UInt8*)&smuSubAddr)[3];
iicPB.combAddr = combAddr; iicPB.dataCount = count;
bcopy(buffer, iicPB.data, count);
rLength = 1; rBuffer[0] = 0xff;
sLength = (short) (&iicPB.data[count] - &iicPB.bus);
#ifdef SMUI2C_DEBUG
{
UInt32 iii;
UInt8 *sBuffer = (UInt8 *)&iicPB;
DLOG("SMUI2C:write1 sL:%d, rL:%d, data:", sLength, rLength);
for (iii = 0; iii < sLength; iii++)
DLOG(" %02x", sBuffer[iii]);
DLOG("\n");
}
#endif
if (kIOReturnSuccess == (status = AppleSMUSendI2CCommand( sLength, (UInt8 *) &iicPB, &rLength, rBuffer, kSMU_I2C_Cmd )))
{
#ifdef SMUI2C_DEBUG
{
UInt32 iii;
DLOG("SMUI2C: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 SMU MID STATUS:%x, retries:%d\n", status, retries);
if (retries >= MAXIICRETRYCOUNT)
status = kIOReturnNotResponding;
if (status == kIOReturnSuccess)
{
IOSleep (kSMUSleepDelay);
for (retries = 0; retries < MAXIICRETRYCOUNT; retries++)
{
iicPB.bus = kI2CStatusBus; rLength = sizeof( rBuffer );
rBuffer[0] = 0xff;
sLength = 1;
#ifdef SMUI2C_DEBUG
{
UInt32 iii;
UInt8 *sBuffer = (UInt8 *)&iicPB;
DLOG("SMUI2C:write3 sL:%d, rL:%d, data:", sLength, rLength);
for (iii = 0; iii < sLength; iii++)
DLOG(" %02x", sBuffer[iii]);
DLOG("\n");
}
#endif
if (kIOReturnSuccess == (status = AppleSMUSendI2CCommand( sLength, (UInt8 *) &iicPB, &rLength, rBuffer, kSMU_I2C_Cmd )))
{
#ifdef SMUI2C_DEBUG
{
UInt32 iii;
DLOG("SMUI2C: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)
status = kIOReturnIOError;
DLOG("WRITE SMU STATUS:0x%x, retries:%02lx\n", status, retries);
cmd->bytesTransfered = count;
break;
}
IOSleep(15);
}
}
DLOG("-IOI2CControllerSMU::processWriteI2CBus\n\n");
return status;
}
IOReturn
IOI2CControllerSMU::AppleSMUSendI2CCommand(
IOByteCount sendLength,
UInt8 *sendBuffer,
IOByteCount *readLength,
UInt8 *readBuffer,
UInt8 command)
{
IOReturn ret = kIOReturnError;
typedef struct
{
IOByteCount sLength;
UInt8 *sBuffer;
IOByteCount *rLength;
UInt8 *rBuffer;
} SMUSendParams;
SMUSendParams params = { sendLength, sendBuffer, readLength, readBuffer };
switch (command)
{
case kSMU_Open: ret = fProvider->callPlatformFunction( i2cOpenSym, true, (void*)¶ms, NULL, NULL, NULL ); break;
case kSMU_I2C_Cmd: ret = fProvider->callPlatformFunction( i2cCommandSym, true, (void*)¶ms, NULL, NULL, NULL ); break;
case kSMU_Close: ret = fProvider->callPlatformFunction( i2cCloseSym, true, (void*)¶ms, NULL, NULL, NULL ); break;
}
return( ret );
}