#include "AppleLM8x.h"
#define super IOService
OSDefineMetaClassAndStructors(AppleLM8x, IOService)
bool AppleLM8x::systemIsRestarting = FALSE;
static const IOPMPowerState ourPowerStates[kLM8xNumStates] =
{
{kIOPMPowerStateVersion1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{kIOPMPowerStateVersion1, kIOPMSleepCapability, kIOPMSleep, kIOPMSleep, 0, 0, 0, 0, 0, 0, 0, 0 },
{kIOPMPowerStateVersion1, kIOPMDeviceUsable, IOPMPowerOn, IOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0}
};
bool AppleLM8x::start(IOService *provider)
{
mach_timespec_t WaitTimeOut;
OSData *t;
int *reg;
IOReturn status;
if ( !(super::start(provider)) )
return false;
systemIsRestarting = FALSE;
if ( !(t = OSDynamicCast(OSData, provider->getProperty("reg"))) )
{
IOLog( "AppleLM8x::start couldn't find 'reg' property in registry.\n");
return false;
}
if ( !(reg = (int*)t->getBytesNoCopy()) )
{
IOLog( "AppleLM8x::start 'reg' property is present but empty.\n");
return false;
}
kLM8xBus = (UInt8)(*reg >> 8); kLM8xAddr = (UInt8)(*reg >> 1);
WaitTimeOut.tv_sec = 30;
WaitTimeOut.tv_nsec = 0;
sOpenI2Cbus = OSSymbol::withCStringNoCopy(kOpenI2Cbus);
sCloseI2Cbus = OSSymbol::withCStringNoCopy(kCloseI2Cbus);
sSetPollingMode = OSSymbol::withCStringNoCopy(kSetPollingMode);
sSetStandardSubMode = OSSymbol::withCStringNoCopy(kSetStandardSubMode);
sSetCombinedMode = OSSymbol::withCStringNoCopy(kSetCombinedMode);
sWriteI2Cbus = OSSymbol::withCStringNoCopy(kWriteI2Cbus);
sReadI2Cbus = OSSymbol::withCStringNoCopy(kReadI2Cbus);
sGetSensorValueSym = OSSymbol::withCString("getSensorValue");
interface = OSDynamicCast(IOService, provider->getParentEntry(gIOServicePlane));
if(interface == NULL)
{
IOLog("AppleLM8x::start(0x%x) failed to get i2c interface\n", kLM8xAddr<<1);
return(false);
}
DLOG("AppleLM8x::start(0x%x) got i2c interface %s\n", kLM8xAddr<<1, interface->getName());
if( initHW(provider) != kIOReturnSuccess )
{
IOLog("AppleLM8x::start(0x%x) failed to initialize sensor.\n", kLM8xAddr<<1);
return false;
}
if ( saveRegisters() != kIOReturnSuccess )
IOLog("AppleLM8x::powerStateWillChangeTo(0x%x) failed to save registers.\n", kLM8xAddr<<1);
PMinit();
status = registerPowerDriver( this, (IOPMPowerState *) ourPowerStates, kLM8xNumStates );
if (status != kIOReturnSuccess)
{
IOLog("%s: Failed to registerPowerDriver.\n", getName());
}
provider->joinPMtree( this);
registerPrioritySleepWakeInterest(&sysPowerDownHandler, this, 0);
registerService();
publishChildren(provider);
return true;
}
void AppleLM8x::stop(IOService *provider)
{
super::stop(provider);
}
IOReturn AppleLM8x::initHW(IOService *provider)
{
IOReturn status;
UInt8 cfgReg;
UInt8 attempts = 0;
DLOG("AppleLM8x::initHW(0x%x) entered.\n", kLM8xAddr<<1);
status = openI2C(kLM8xBus);
if(status != kIOReturnSuccess)
{
DLOG("AppleLM8x::initHW(0x%x) failed to open I2C bus.\n", kLM8xAddr<<1);
return status;
}
while( attempts < kTriesToAttempt)
{
cfgReg = 0;
status = readI2C((UInt8)kConfReg1, (UInt8 *) &cfgReg, 1);
if(status != kIOReturnSuccess)
{
DLOG("AppleLM8x::initHW(0x%x) readI2C failed.\n", kLM8xAddr<<1);
closeI2C();
return status;
}
if((cfgReg & 0x10) == 0x10)
status = kIOReturnError;
if(status == kIOReturnSuccess)
{
DLOG("AppleLM8x::readI2C(0x%x) retrieved data = 0x%x\n", kLM8xAddr<<1, *data);
break;
}
attempts++;
IOSleep(10); }
if(status != kIOReturnSuccess)
{
IOLog("AppleLM8x::initHW(0x%x) readI2C failed due to persistant RESET bit.\n", kLM8xAddr<<1);
closeI2C();
return status;
}
cfgReg |= 0x1;
status = writeI2C((UInt8)kConfReg1, (UInt8 *)&cfgReg, 1);
closeI2C();
return status;
}
IOReturn AppleLM8x::getReading(UInt32 subAddress, SInt32 *value)
{
IOReturn status;
UInt8 data;
if (systemIsRestarting == TRUE)
return kIOReturnOffline;
status = openI2C(kLM8xBus);
if(status != kIOReturnSuccess)
{
DLOG("AppleLM8x::getReading(0x%x) failed to open I2C bus.\n", kLM8xAddr<<1);
return status;
}
status = readI2C((UInt8)subAddress, (UInt8 *) &data, 1);
if(status != kIOReturnSuccess)
{
DLOG("AppleLM8x::getReading(0x%x) readI2C failed.\n", kLM8xAddr<<1);
closeI2C();
return status;
}
closeI2C();
*value = (SInt32)data;
return kIOReturnSuccess;
}
#pragma mark -
#pragma mark *** Platform Functions ***
#pragma mark -
IOReturn AppleLM8x::callPlatformFunction
(
const OSSymbol *functionName,
bool waitForFunction,
void *param1,
void *param2,
void *param3,
void *param4
)
{
UInt32 reg = (UInt32)param1;
SInt32 *value = (SInt32 *)param2;
DLOG
(
"AppleLM8x::callPlatformFunction(0x%x) %s %s %08lx %08lx %08lx %08lx\n",
kLM8xAddr<<1,
functionName->getCStringNoCopy(), waitForFunction ? "TRUE" : "FALSE",
(UInt32)param1,
(UInt32)param2,
(UInt32)param3,
(UInt32)param4
);
if (functionName->isEqualTo(sGetSensorValueSym) == TRUE)
{
IOReturn status;
if (systemIsRestarting == TRUE)
return(kIOReturnOffline);
for(int i = 0; i < LUNtableElement; i++)
{
if(reg == LUNtable[i].SubAddress)
{
status = getReading((UInt8)LUNtable[i].SubAddress, value);
if(LUNtable[i].type == kTypeTemperature)
{
UInt8 reading = *value;
*value = ( ( ( ( SInt8 ) reading ) << 16 ) * LUNtable[i].ConversionMultiple );
}
if(LUNtable[i].type == kTypeADC)
*value = (UInt32)(*value * LUNtable[i].ConversionMultiple);
if(LUNtable[i].type == kTypeVoltage)
*value = (UInt32)(*value * LUNtable[i].ConversionMultiple);
return status;
}
}
}
return (super::callPlatformFunction(functionName, waitForFunction, param1, param2, param3, param4));
}
IOReturn AppleLM8x::publishChildren(IOService *nub)
{
OSIterator *childIterator = NULL;
IORegistryEntry *childEntry = NULL;
IOService *childNub = NULL;
IOReturn status;
childIterator = nub->getChildIterator(gIODTPlane);
if( childIterator != NULL )
{
while ( ( childEntry = (IORegistryEntry *)( childIterator->getNextObject() ) ) != NULL )
{
status = buildEntryTable(childEntry);
LUNtableElement++;
childNub = OSDynamicCast(IOService, OSMetaClass::allocClassWithName("IOService"));
childNub->init(childEntry, gIODTPlane);
childNub->attach(this);
childNub->registerService();
}
childIterator->release();
}
return kIOReturnSuccess;
}
IOReturn AppleLM8x::buildEntryTable(IORegistryEntry *child)
{
OSData *data;
data = OSDynamicCast(OSData, child->getProperty("reg"));
if(data)
LUNtable[LUNtableElement].SubAddress = *(UInt32 *)data->getBytesNoCopy();
if (LUNtable[LUNtableElement].SubAddress == 0x20) {
data = OSDynamicCast(OSData, child->getProperty("device_type"));
if (data)
{
char *ptr = (char *)data->getBytesNoCopy();
if(strcmp(ptr, "adc-sensor") == 0) {
LUNtable[LUNtableElement].ConversionMultiple = k25VinMultiplier;
LUNtable[LUNtableElement].type = kTypeADC;
}
else if(strcmp(ptr, "voltage-sensor") == 0)
{
LUNtable[LUNtableElement].ConversionMultiple = k25VinMultiplier;
LUNtable[LUNtableElement].type = kTypeVoltage;
}
else
{
LUNtable[LUNtableElement].ConversionMultiple = 1;
LUNtable[LUNtableElement].type = kTypeTemperature;
}
}
}
else if (LUNtable[LUNtableElement].SubAddress == 0x21) {
LUNtable[LUNtableElement].ConversionMultiple = Vccp1Multiplier;
LUNtable[LUNtableElement].type = kTypeADC;
}
else if (LUNtable[LUNtableElement].SubAddress == 0x22) {
LUNtable[LUNtableElement].ConversionMultiple = kVccMultiplier;
LUNtable[LUNtableElement].type = kTypeADC;
}
else if (LUNtable[LUNtableElement].SubAddress == 0x23) {
LUNtable[LUNtableElement].ConversionMultiple = k5VinMultiplier;
LUNtable[LUNtableElement].type = kTypeADC;
}
else if (LUNtable[LUNtableElement].SubAddress == 0x24) {
LUNtable[LUNtableElement].ConversionMultiple = k12VinMultiplier;
LUNtable[LUNtableElement].type = kTypeADC;
}
else if (LUNtable[LUNtableElement].SubAddress == 0x25) {
LUNtable[LUNtableElement].ConversionMultiple = Vccp2Multiplier;
LUNtable[LUNtableElement].type = kTypeADC;
}
else if (LUNtable[LUNtableElement].SubAddress == 0x26) {
LUNtable[LUNtableElement].ConversionMultiple = 1;
LUNtable[LUNtableElement].type = kTypeTemperature;
}
else if (LUNtable[LUNtableElement].SubAddress == 0x27) {
LUNtable[LUNtableElement].ConversionMultiple = 1;
LUNtable[LUNtableElement].type = kTypeTemperature;
}
else if (LUNtable[LUNtableElement].SubAddress == 0x28) {
data = OSDynamicCast(OSData, child->getProperty("device_type"));
if (data)
{
char *ptr = (char *)data->getBytesNoCopy();
if(strcmp(ptr, "adc-sensor") == 0) {
LUNtable[LUNtableElement].ConversionMultiple = AIN1Multiplier;
LUNtable[LUNtableElement].type = kTypeADC;
}
else if(strcmp(ptr, "voltage-sensor") == 0)
{
LUNtable[LUNtableElement].ConversionMultiple = AIN1Multiplier;
LUNtable[LUNtableElement].type = kTypeVoltage;
}
else
{
LUNtable[LUNtableElement].ConversionMultiple = 0; LUNtable[LUNtableElement].type = kTypeRPM;
}
}
}
else if (LUNtable[LUNtableElement].SubAddress == 0x29) {
data = OSDynamicCast(OSData, child->getProperty("device_type"));
if (data)
{
char *ptr = (char *)data->getBytesNoCopy();
if(strcmp(ptr, "adc-sensor") == 0) {
LUNtable[LUNtableElement].ConversionMultiple = AIN2Multiplier;
LUNtable[LUNtableElement].type = kTypeADC;
}
else if(strcmp(ptr, "voltage-sensor") == 0)
{
LUNtable[LUNtableElement].ConversionMultiple = AIN2Multiplier;
LUNtable[LUNtableElement].type = kTypeVoltage;
}
else
{
LUNtable[LUNtableElement].ConversionMultiple = 0; LUNtable[LUNtableElement].type = kTypeRPM;
}
}
}
return kIOReturnSuccess;
}
#pragma mark -
#pragma mark *** I2C ***
#pragma mark -
IOReturn AppleLM8x::openI2C(UInt8 id)
{
IOReturn status;
DLOG("AppleLM8x::openI2C(0x%x) entered.\n", kLM8xAddr<<1);
if (interface == NULL)
{
DLOG("AppleLM8x::openI2C(0x%x) interface is NULL.\n", kLM8xAddr<<1);
return kIOReturnBadArgument;
}
status = interface->callPlatformFunction(sOpenI2Cbus, false, (void *)((UInt32)id), NULL, NULL, NULL);
if (status != kIOReturnSuccess)
{
DLOG("AppleLM8x::openI2C(0x%x) failed on 'OpenI2Cbus' command.\n", kLM8xAddr<<1);
return status;
}
status = interface->callPlatformFunction(sSetPollingMode, false, (void *)TRUE, NULL, NULL, NULL);
if (status != kIOReturnSuccess)
{
DLOG("AppleLM8x::openI2C(0x%x) failed to set polling mode, status = %08lx\n", kLM8xAddr<<1, (UInt32) status);
return status;
}
return kIOReturnSuccess;
}
IOReturn AppleLM8x::closeI2C( void )
{
IOReturn status;
DLOG("AppleLM8x::closeI2C(0x%x) entered.\n", kLM8xAddr<<1);
if (interface == NULL)
{
DLOG("AppleLM8x::closeI2C(0x%x) interface is NULL.\n", kLM8xAddr<<1);
return kIOReturnBadArgument;
}
status = interface->callPlatformFunction(sCloseI2Cbus, false, NULL, NULL, NULL, NULL);
if (status != kIOReturnSuccess)
{
DLOG("AppleLM8x::closeI2C(0x%x) failed on 'closeI2Cbus' command.\n", kLM8xAddr<<1);
return status;
}
return kIOReturnSuccess;
}
IOReturn AppleLM8x::writeI2C(UInt8 subAddr, UInt8 *data, UInt16 size)
{
IOReturn status;
UInt8 attempts = 0;
DLOG("AppleLM8x::writeI2C(0x%x) entered.\n", kLM8xAddr<<1);
if ( (interface == NULL) || (data == NULL) || (size == 0) )
{
DLOG("AppleLM8x::writeI2C(0x%x) called with incorrect function parameters.\n", kLM8xAddr<<1);
return kIOReturnBadArgument;
}
status = interface->callPlatformFunction(sSetStandardSubMode, false, NULL, NULL, NULL, NULL);
if (status != kIOReturnSuccess)
{
DLOG("AppleLM8x::readI2C(0x%x) failed to set 'StandardSub' mode.\n", kLM8xAddr<<1);
return status;
}
while(attempts < kTriesToAttempt)
{
status =
interface->callPlatformFunction
(
sWriteI2Cbus,
false,
(void *)((UInt32)kLM8xAddr),
(void *)((UInt32)subAddr),
(void *)data,
(void *)((UInt32)size)
);
if(status == kIOReturnSuccess)
break;
attempts++;
IOSleep(10); }
if(status != kIOReturnSuccess)
{
IOLog("AppleLM8x::writeI2C(0x%x) failed %d attempts of the 'writeI2C' command for subaddr 0x%x.\n", kLM8xAddr<<1, attempts, subAddr);
return status;
}
DLOG("AppleLM8x::writeI2C(0x%x) wrote data = 0x%x\n", kLM8xAddr<<1, *data);
return status;
}
IOReturn AppleLM8x::readI2C(UInt8 subAddr, UInt8 *data, UInt16 size)
{
IOReturn status;
UInt8 attempts = 0;
DLOG("AppleLM8x::readI2C(0x%x) entered.\n", kLM8xAddr<<1);
if ( (interface == NULL) || (data == NULL) || (size == 0) )
{
DLOG("AppleLM8x::readI2C(0x%x) called with incorrect function parameters.\n", kLM8xAddr<<1);
return kIOReturnBadArgument;
}
status = interface->callPlatformFunction(sSetCombinedMode, false, NULL, NULL, NULL, NULL);
if (status != kIOReturnSuccess)
{
DLOG("AppleLM8x::readI2C(0x%x) failed to set 'Combined' mode.\n", kLM8xAddr<<1);
return status;
}
while( attempts < kTriesToAttempt)
{
status =
interface->callPlatformFunction
(
sReadI2Cbus,
false,
(void *)((UInt32)kLM8xAddr),
(void *)((UInt32)subAddr),
(void *)data,
(void *)((UInt32)size)
);
if(*data == 0xFF)
status = kIOReturnError;
if(status == kIOReturnSuccess)
break;
attempts++;
IOSleep(10); }
if(*data == 0xFF)
{
IOLog("AppleLM8x::readI2C(0x%x) failed %d attempts of the 'readI2C' command for subaddr 0x%x (BOGUS DATA)\n", kLM8xAddr<<1, attempts, subAddr);
return status;
}
if(status != kIOReturnSuccess)
{
IOLog("AppleLM8x::readI2C(0x%x) failed %d attempts of the 'readI2C' command for subaddr 0x%x.\n", kLM8xAddr<<1, attempts, subAddr);
return status;
}
DLOG("AppleLM8x::readI2C(0x%x) retrieved data 0x%x from subaddr 0x%x\n", kLM8xAddr<<1, *data, subAddr);
return status;
}
#pragma mark -
#pragma mark *** Power Management ***
#pragma mark -
IOReturn AppleLM8x::setPowerState(unsigned long whatState, IOService *dontCare)
{
DLOG("AppleLM8x::setPowerState called with state:%lu\n", whatState);
if ( (whatState == kLM8xOffState) )
{
systemIsRestarting = TRUE;
if ( saveRegisters() != kIOReturnSuccess )
IOLog("AppleLM8x::powerStateWillChangeTo(0x%x) failed to save registers.\n", kLM8xAddr<<1);
}
if ( (whatState == kLM8xOnState) )
{
if ( restoreRegisters() != kIOReturnSuccess )
IOLog("AppleLM8x::powerStateWillChangeTo(0x%x) failed to restore registers.\n", kLM8xAddr<<1);
systemIsRestarting = FALSE; }
return IOPMAckImplied;
}
IOReturn AppleLM8x::sysPowerDownHandler(void *target, void *refCon, UInt32 messageType, IOService *service, void *messageArgument, vm_size_t argSize )
{
IOReturn status = kIOReturnSuccess;
DLOG("AppleLM8x::sysPowerDownHandler called with 0x%x\n", messageType);
switch (messageType)
{
case kIOMessageSystemWillSleep:
break;
case kIOMessageSystemWillPowerOff: case kIOMessageSystemWillRestart: systemIsRestarting = TRUE; break;
default:
status = kIOReturnUnsupported;
break;
}
return status;
}
IOReturn AppleLM8x::saveRegisters()
{
IOReturn status;
DLOG("AppleLM8x::saveRegisters(0x%x) entered.\n", kLM8xAddr<<1);
status = openI2C(kLM8xBus);
if(status != kIOReturnSuccess)
{
DLOG("AppleLM8x::saveRegisters(0x%x) failed to open I2C bus.\n", kLM8xAddr<<1);
return status;
}
status = readI2C((UInt8)kChannelModeRegister, (UInt8 *)&savedRegisters.ChannelMode, 1);
if(status != kIOReturnSuccess)
{
DLOG("AppleLM8x::saveRegisters(0x%x) readI2C failed.\n", kLM8xAddr<<1);
closeI2C();
return status;
}
status = readI2C((UInt8)kConfReg1, (UInt8 *)&savedRegisters.Configuration1, 1);
if(status != kIOReturnSuccess)
{
DLOG("AppleLM8x::saveRegisters(0x%x) readI2C failed.\n", kLM8xAddr<<1);
closeI2C();
return status;
}
status = readI2C((UInt8)kConfReg2, (UInt8 *)&savedRegisters.Configuration2, 1);
if(status != kIOReturnSuccess)
{
DLOG("AppleLM8x::saveRegisters(0x%x) readI2C failed.\n", kLM8xAddr<<1);
closeI2C();
return status;
}
closeI2C();
return status;
}
IOReturn AppleLM8x::restoreRegisters()
{
IOReturn status;
DLOG("AppleLM8x::restoreRegisters(0x%x) entered.\n", kLM8xAddr<<1);
status = openI2C(kLM8xBus);
if(status != kIOReturnSuccess)
{
DLOG("AppleLM8x::restoreRegisters(0x%x) failed to open I2C bus.\n", kLM8xAddr<<1);
return status;
}
status = writeI2C((UInt8)kChannelModeRegister, (UInt8 *)&savedRegisters.ChannelMode, 1);
if(status != kIOReturnSuccess)
{
DLOG("AppleLM8x::restoreRegisters(0x%x) writeI2C failed.\n", kLM8xAddr<<1);
closeI2C();
return status;
}
status = writeI2C((UInt8)kConfReg2, (UInt8 *)&savedRegisters.Configuration2, 1);
if(status != kIOReturnSuccess)
{
DLOG("AppleLM8x::restoreRegisters(0x%x) writeI2C failed.\n", kLM8xAddr<<1);
closeI2C();
return status;
}
status = writeI2C((UInt8)kConfReg1, (UInt8 *)&savedRegisters.Configuration1, 1);
if(status != kIOReturnSuccess)
{
DLOG("AppleLM8x::restoreRegisters(0x%x) writeI2C failed.\n", kLM8xAddr<<1);
closeI2C();
return status;
}
closeI2C();
return status;
}