#include <IOKit/IODeviceTreeSupport.h>
#include "IOI2CADT746x.h"
#define super IOI2CDevice
OSDefineMetaClassAndStructors(IOI2CADT746x, IOI2CDevice)
bool IOI2CADT746x::start(IOService *provider)
{
IOService *childNub;
OSArray * nubArray;
if (false == super::start(provider))
return false;
if (kIOReturnSuccess != readI2C(kDeviceIDReg, &fDeviceID, 1))
{
ERRLOG("IOI2CADT746x@%lx::start device not responding!\n", getI2CAddress());
freeI2CResources();
return false;
}
nubArray = parseSensorParamsAndCreateNubs(provider);
if (nubArray == NULL || nubArray->getCount() == 0)
{
ERRLOG("-IOI2CADT746x@%lx::start no thermal sensors found\n", getI2CAddress());
if (nubArray)
nubArray->release();
freeI2CResources();
return(false);
}
for (unsigned int index = 0; index < nubArray->getCount(); index++)
{
childNub = OSDynamicCast(IOService, nubArray->getObject(index));
if (childNub) childNub->registerService();
}
nubArray->release();
registerService();
return true;
}
OSArray * IOI2CADT746x::parseSensorParamsAndCreateNubs(IOService *provider)
{
IOService *nub;
OSData *tmp_osdata;
OSArray *nubArray = NULL;
unsigned i, n_sensors = 0, nubs_created = 0;
UInt32 version, *id = NULL, *zone = NULL, *polling_period = NULL;
const char *type = NULL, *location = NULL;
char work[32];
if (NULL == (tmp_osdata = OSDynamicCast(OSData, provider->getProperty(kDTSensorParamsVersionKey))))
return NULL;
version = *((UInt32 *)tmp_osdata->getBytesNoCopy());
if (version == 2) {
IORegistryEntry * deviceNub;
OSIterator * kids;
if (kids = provider->getChildIterator( gIODTPlane ))
{
while ( deviceNub = (IORegistryEntry *)kids->getNextObject() )
{
nub = new IOService;
nub->init( deviceNub, gIODTPlane );
nub->attach(this);
if (nubArray == NULL)
nubArray = OSArray::withObjects((const OSObject **) &nub, 1);
else
nubArray->setObject( nub );
}
kids->release();
}
return nubArray;
}
else
if (version != 1)
{
ERRLOG("IOI2CADT746x::parseSensorParamsAndCreateNubs(0x%lx) version != 1\n", getI2CAddress());
return(NULL);
}
if (NULL == (tmp_osdata = OSDynamicCast(OSData, provider->getProperty(kDTSensorIDKey))))
return NULL;
n_sensors = tmp_osdata->getLength() / sizeof(UInt32);
if (n_sensors > 6)
return NULL;
id = (UInt32 *)tmp_osdata->getBytesNoCopy();
if (NULL == (tmp_osdata = OSDynamicCast(OSData, provider->getProperty(kDTSensorZoneKey))))
return NULL;
zone = (UInt32 *)tmp_osdata->getBytesNoCopy();
if (NULL == (tmp_osdata = OSDynamicCast(OSData, provider->getProperty(kDTSensorTypeKey))))
return NULL;
type = (const char *)tmp_osdata->getBytesNoCopy();
if (NULL == (tmp_osdata = OSDynamicCast(OSData, provider->getProperty(kDTSensorLocationKey))))
return NULL;
location = (const char *)tmp_osdata->getBytesNoCopy();
if (tmp_osdata = OSDynamicCast(OSData, provider->getProperty(kDTSensorPollingPeriodKey)))
polling_period = (UInt32 *)tmp_osdata->getBytesNoCopy();
for (i=0; i<n_sensors; i++)
{
nub = new IOService;
if (!nub || !nub->init())
continue;
if (!strcmp(type, kDTTemperatureSensorType))
{
strcpy(work, kHWSensorTemperatureNubName);
nub->setName(kHWSensorTemperatureNubName);
}
else if (!strcmp(type, kDTVoltageSensorType))
{
strcpy(work, kHWSensorVoltageNubName);
nub->setName(kHWSensorVoltageNubName);
}
else if (!strcmp(type, kDTFanSpeedSensorType))
{
strcpy(work, kHWSensorFanSpeedNubName);
nub->setName(kHWSensorFanSpeedNubName);
}
else
return NULL;
tmp_osdata = OSData::withBytes(work, strlen(work) + 1);
if (tmp_osdata == NULL)
return NULL;
nub->attach(this);
fHWSensorIDMap[i] = id[i];
nub->setProperty("name", tmp_osdata);
nub->setProperty("compatible", tmp_osdata);
nub->setProperty("device_type", tmp_osdata);
nub->setProperty(kHWSensorParamsVersionKey, &version, sizeof(UInt32));
nub->setProperty(kHWSensorIDKey, &id[i], sizeof(UInt32));
nub->setProperty(kHWSensorZoneKey, &zone[i], sizeof(UInt32));
nub->setProperty(kHWSensorTypeKey, type);
type += strlen(type) + 1;
nub->setProperty(kHWSensorLocationKey, location);
location += strlen(location) + 1;
if (polling_period != NULL)
{
if (polling_period[i] != kHWSensorPollingPeriodNULL)
nub->setProperty(kHWSensorPollingPeriodKey, &polling_period[i],
sizeof(UInt32));
}
nubs_created++;
if (nubArray == NULL)
nubArray = OSArray::withObjects((const OSObject **) &nub, 1);
else
nubArray->setObject( nub );
}
tmp_osdata->release();
return nubArray;
}
IOReturn IOI2CADT746x::callPlatformFunction(const OSSymbol *functionName,
bool waitForFunction, void *param1, void *param2,
void *param3, void *param4)
{
UInt32 id = (UInt32)param1;
SInt32 *temp_buf = (SInt32 *)param2;
UInt8 statusByte;
UInt32 key;
if (0 == functionName)
{
ERRLOG("@IOI2CADT746x::callPlatformFunction bad arg!\n");
return kIOReturnBadArgument;
}
if (functionName->isEqualTo(kGetSensorValueSymbol) == true)
{
if (fClearSMBAlertStatus == true)
{
IOReturn status;
if (kIOReturnSuccess == (status = lockI2CBus(&key)))
{
if (kIOReturnSuccess != (status = readI2C(kIntStatusReg1, &statusByte, 1, key)))
ERRLOG("@IOI2CADT746x::CPF error reading I2C reg:0x%x: 0x%lx\n", kIntStatusReg1, (UInt32)status);
else
if (kIOReturnSuccess != (status = readI2C(kIntStatusReg2, &statusByte, 1, key)))
ERRLOG("@IOI2CADT746x::CPF error reading I2C reg:0x%x: 0x%lx\n", kIntStatusReg2, (UInt32)status);
unlockI2CBus(key);
}
else
{
ERRLOG("@IOI2CADT746x::CPF error locking I2C bus: 0x%lx\n", (UInt32)status);
return status;
}
fClearSMBAlertStatus = false;
}
if (id == fHWSensorIDMap[0])
{
return(getLocalTemp(temp_buf));
}
else if (id == fHWSensorIDMap[1])
{
return(getRemote1Temp(temp_buf));
}
else if (id == fHWSensorIDMap[2])
{
return(getRemote2Temp(temp_buf));
}
else if (id == fHWSensorIDMap[3])
{
return(getVoltage(temp_buf));
}
else if (id == fHWSensorIDMap[4])
{
return(getFanTach(temp_buf, kFanTachOne));
}
else if (id == fHWSensorIDMap[5])
{
return(getFanTach(temp_buf, kFanTachTwo));
}
}
return(super::callPlatformFunction(functionName, waitForFunction,
param1, param2, param3, param4));
}
IOReturn IOI2CADT746x::getFanTach(SInt32 *fanSpeed, SInt16 whichFan)
{
UInt8 tachLowByteAddr, tachHighByteAddr;
UInt8 tachLowByte, tachHighByte;
UInt16 fullValue;
UInt8 fanPulsePerRev;
UInt16 fanWhole;
IOReturn status;
UInt32 key;
if (fanSpeed == NULL)
return kIOReturnBadArgument;
if (whichFan == kFanTachOne)
{
tachLowByteAddr = kTACH1LowByte;
tachHighByteAddr = kTACH1HighByte;
}
else if (whichFan == kFanTachTwo)
{
tachLowByteAddr = kTACH2LowByte;
tachHighByteAddr = kTACH2HighByte;
}
else {
*fanSpeed = -1;
return kIOReturnUnsupported;
}
if (kIOReturnSuccess == (status = lockI2CBus(&key)))
{
if (kIOReturnSuccess != (status = readI2C(tachLowByteAddr, &tachLowByte, 1, key)))
ERRLOG("IOI2CADT746x@%lx::getFanTach i2c read low A:0x%x error:0x%x\n", getI2CAddress(), tachLowByteAddr, status);
else if (kIOReturnSuccess != (status = readI2C(tachHighByteAddr, &tachHighByte, 1, key)))
ERRLOG("IOI2CADT746x@%lx::getFanTach i2c read high A:0x%x error:0x%x\n", getI2CAddress(), tachHighByteAddr, status);
else if (kIOReturnSuccess != (status = readI2C(kFanPulsePerRev, &fanPulsePerRev, 1, key)))
ERRLOG("IOI2CADT746x@%lx::getFanTach i2c read ppr A:0x%x error:0x%x\n", getI2CAddress(), kFanPulsePerRev, status);
unlockI2CBus(key);
}
if (kIOReturnSuccess != status)
{
*fanSpeed = -1;
return status;
}
fullValue = ((UInt16)tachHighByte << 8) | tachLowByte;
if ((fullValue == 0xFFFF) || (fullValue == 0x0)) fanWhole = 0;
else
fanWhole = ((90000 * 60) / fullValue);
*fanSpeed = (fanWhole << 16);
return kIOReturnSuccess;
}
IOReturn IOI2CADT746x::getVoltage(SInt32 *voltage)
{
UInt8 fullByte, ext, dummybyte, config4, config2;
UInt32 volIndex;
UInt32 incrementsPerVolt;
UInt8 vccReadAddr;
IOReturn status;
UInt32 key;
if (voltage == NULL)
return kIOReturnBadArgument;
if (kIOReturnSuccess == (status = lockI2CBus(&key)))
{
if (fDeviceID == 0xFF) status = readI2C(kDeviceIDReg, &fDeviceID, 1, key);
vccReadAddr = ((fDeviceID == kDeviceIDADT7460) ? k2_5VReading : k2_5VccpReading );
if (kIOReturnSuccess != (status = readI2C(kConfigReg4, &config4, 1, key)))
ERRLOG("IOI2CADT746x@%lx::getVoltage i2c read config1 A:0x%x error:0x%x\n", getI2CAddress(), kConfigReg1, status);
else if (kIOReturnSuccess != (status = readI2C(kConfigReg2, &config2, 1, key)))
ERRLOG("IOI2CADT746x@%lx::getVoltage i2c read config2 A:0x%x error:0x%x\n", getI2CAddress(), kConfigReg2, status);
else if (kIOReturnSuccess != (status = readI2C(kExtendedRes1, &ext, 1, key)))
ERRLOG("IOI2CADT746x@%lx::getVoltage i2c read kExtendedRes1 A:0x%x error:0x%x\n", getI2CAddress(), kExtendedRes1, status);
else if (kIOReturnSuccess != (status = readI2C(vccReadAddr, &fullByte, 1, key)))
ERRLOG("IOI2CADT746x@%lx::getVoltage i2c read vccReadAddr A:0x%x error:0x%x\n", getI2CAddress(), vccReadAddr, status);
else if (kIOReturnSuccess != (status = readI2C(kVccReading, &dummybyte, 1, key)))
ERRLOG("IOI2CADT746x@%lx::getVoltage i2c read kVccReading A:0x%x error:0x%x\n", getI2CAddress(), kVccReading, status);
unlockI2CBus(key);
}
if (kIOReturnSuccess != status)
{
return status;
}
volIndex = (SInt32) VOLTAGE_INDEX_FROM_BYTES(fullByte, ext) * 100;
if (fDeviceID == kDeviceIDADT7460) {
if ((config2 & k2_5VAttenuationMask) != 0)
incrementsPerVolt = kIncrementPerVolt2_25Max;
else
incrementsPerVolt = kIncrementPerVolt3_3Max;
}
else {
if (((config2 & k2_5VAttenuationMask) != 0) || ((kConfigReg4 & k2_5VAttenuationMask) != 0))
incrementsPerVolt = kUnitsPerVoltWithAttenuation7467;
else
incrementsPerVolt = kUnitsPerVoltWithoutAttenuation7467;
}
*voltage = ((volIndex << 16) / incrementsPerVolt);
return kIOReturnSuccess;
}
IOReturn IOI2CADT746x::getLocalTemp(SInt32 *temperature)
{
SInt8 highbyte;
UInt8 lowbyte, ext, dummybyte;
SInt32 sensor_value;
IOReturn status;
UInt32 key;
if (temperature == NULL)
return kIOReturnBadArgument;
if (kIOReturnSuccess == (status = lockI2CBus(&key)))
{
if (kIOReturnSuccess != (status = readI2C(kExtendedRes2, &ext, 1, key)))
ERRLOG("IOI2CADT746x@%lx::getLocalTemp i2c read kExtendedRes2 A:0x%x error:0x%x\n", getI2CAddress(), kExtendedRes2, status);
else if (kIOReturnSuccess != (status = readI2C(kLocalTemperature, (UInt8 *)&highbyte, 1, key)))
ERRLOG("IOI2CADT746x@%lx::getLocalTemp i2c read kLocalTemperature A:0x%x error:0x%x\n", getI2CAddress(), kLocalTemperature, status);
else if (kIOReturnSuccess != (status = readI2C(kRemote1Temp, &dummybyte, 1, key)))
ERRLOG("IOI2CADT746x@%lx::getLocalTemp i2c read kRemote1Temp A:0x%x error:0x%x\n", getI2CAddress(), kRemote1Temp, status);
else if (kIOReturnSuccess != (status = readI2C(kRemote2Temp, &dummybyte, 1, key)))
ERRLOG("IOI2CADT746x@%lx::getLocalTemp i2c read kRemote2Temp A:0x%x error:0x%x\n", getI2CAddress(), kRemote2Temp, status);
unlockI2CBus(key);
}
if (kIOReturnSuccess != status)
return status;
lowbyte = LOCAL_FROM_EXT_TEMP(ext);
sensor_value = SIGNED_TEMP_FROM_BYTES(highbyte, lowbyte);
*temperature = sensor_value;
return kIOReturnSuccess;
}
IOReturn IOI2CADT746x::getRemote1Temp(SInt32 *temperature)
{
SInt8 highbyte;
UInt8 lowbyte, ext, dummybyte;
SInt32 sensor_value;
IOReturn status;
if (temperature == NULL)
return kIOReturnBadArgument;
if (kIOReturnSuccess != (status = readI2C(kExtendedRes2, &ext, 1)))
ERRLOG("IOI2CADT746x::getRemote1Temp error:0x%lx reading I2C reg:%d\n", (UInt32)status, kExtendedRes2);
if (kIOReturnSuccess != (status = readI2C(kRemote1Temp, (UInt8 *) &highbyte, 1)))
ERRLOG("IOI2CADT746x::getRemote1Temp error:0x%lx reading I2C reg:%d\n", (UInt32)status, kRemote1Temp);
readI2C(kLocalTemperature, &dummybyte, 1); readI2C(kRemote2Temp, &dummybyte, 1);
if (status != kIOReturnSuccess)
return status;
lowbyte = REMOTE1_FROM_EXT_TEMP(ext);
sensor_value = SIGNED_TEMP_FROM_BYTES(highbyte, lowbyte);
*temperature = sensor_value;
return kIOReturnSuccess;
}
IOReturn IOI2CADT746x::getRemote2Temp(SInt32 *temperature)
{
SInt8 highbyte;
UInt8 lowbyte, ext, dummybyte;
SInt32 sensor_value;
IOReturn status;
if (temperature == NULL)
return kIOReturnBadArgument;
if (kIOReturnSuccess != (status = readI2C(kExtendedRes2, &ext, 1)))
ERRLOG("IOI2CADT746x::getRemote2Temp error:0x%lx reading I2C reg:%d\n", (UInt32)status, kExtendedRes2);
if (kIOReturnSuccess != (status = readI2C(kRemote2Temp, (UInt8 *) &highbyte, 1)))
ERRLOG("IOI2CADT746x::getRemote2Temp error:0x%lx reading I2C reg:%d\n", (UInt32)status, kRemote2Temp);
readI2C(kRemote1Temp, &dummybyte, 1); readI2C(kLocalTemperature, &dummybyte, 1);
if (status != kIOReturnSuccess)
return status;
lowbyte = REMOTE2_FROM_EXT_TEMP(ext);
sensor_value = SIGNED_TEMP_FROM_BYTES(highbyte, lowbyte);
*temperature = sensor_value;
return kIOReturnSuccess;
}
void IOI2CADT746x::processPowerEvent(UInt32 eventType)
{
switch (eventType)
{
case kI2CPowerEvent_ON:
case kI2CPowerEvent_WAKE:
fClearSMBAlertStatus = true; break;
}
}