IOI2CDriveBayMGPIO.cpp [plain text]
#include <IOKit/IOLib.h>
#include <IOKit/IODeviceTreeSupport.h>
#include "IOI2CDriveBayMGPIO.h"
#include "IOI2CDriveBayGPIO.h"
#include "GPIOParent.h"
#include "IOPlatformFunction.h"
#ifdef DLOG
#undef DLOG
#endif
#define IOI2CPCA9554_DEBUG 1
#ifdef IOI2CPCA9554_DEBUG
#define DLOG(fmt, args...) kprintf(fmt, ## args)
#else
#define DLOG(fmt, args...)
#endif
#define LOCK IOLockLock(fClientLock)
#define UNLOCK IOLockUnlock(fClientLock)
#define super IOI2CDevice
OSDefineMetaClassAndStructors(IOI2CDriveBayMGPIO, IOI2CDevice)
bool
IOI2CDriveBayMGPIO::start(IOService *provider)
{
IOReturn status = kIOReturnError;
OSData *data;
mach_timespec_t timeout;
fSymIntRegister = OSSymbol::withCString(kIOPFInterruptRegister);
fSymIntUnRegister = OSSymbol::withCString(kIOPFInterruptUnRegister);
fSymIntEnable = OSSymbol::withCString(kIOPFInterruptEnable);
fSymIntDisable = OSSymbol::withCString(kIOPFInterruptDisable);
fC3Mapping = true;
if (0 == (data = OSDynamicCast(OSData, provider->getProperty("reg"))))
{
DLOG ("IOI2CDriveBayMGPIO::start - reg property not found\n");
return false;
}
fIntAddrInfo = *((UInt32 *)data->getBytesNoCopy());
fIntAddrInfo <<= 8; fIntAddrInfo |= 0x00000100;
fConfigReg = 0xFF; fPolarityReg = 0x00;
if (data = OSDynamicCast(OSData, provider->getProperty("config-reg")))
fConfigReg = (UInt8)*((UInt32 *)data->getBytesNoCopy());
if (data = OSDynamicCast(OSData, provider->getProperty("polarity-reg")))
fPolarityReg = (UInt8)*((UInt32 *)data->getBytesNoCopy());
DLOG("IOI2CDriveBayMGPIO@%lx::start fConfigReg = %02x fPolarityReg = %02x\n", fIntAddrInfo, fConfigReg, fPolarityReg);
fClientCount = 0;
for (int i = 0; i < kCLIENT_MAX; i++)
fClient[i].reg = 0x4badbeef;
if (0 == (fClientLock = IOLockAlloc()))
return false;
if (data = OSDynamicCast(OSData, provider->getProperty("platform-drivebay-sense")))
{
DLOG ("IOI2CDriveBayMGPIO::start - got platform-drivebay-sense property!\n");
if (data = OSDynamicCast(OSData, provider->getProperty("AAPL,phandle")))
{
DLOG ("IOI2CDriveBayMGPIO::start - got AAPL,phandle property!\n");
char cstr[256];
UInt32 ph;
ph = *(UInt32 *)data->getBytesNoCopy();
snprintf( cstr, sizeof(cstr), "platform-drivebay-sense-%08lx", ph );
if (fDrivebaySenseSym = OSSymbol::withCString(cstr))
{
timeout.tv_sec = 30;
timeout.tv_nsec = 0;
if (fDrivebaySense = waitForService(resourceMatching(fDrivebaySenseSym), &timeout))
{
fDrivebaySense = OSDynamicCast( IOService, fDrivebaySense->getProperty(fDrivebaySenseSym) );
if (fDrivebaySense)
{
status = fDrivebaySense->callPlatformFunction(fDrivebaySenseSym, TRUE, (void *)&IOI2CDriveBayMGPIO::sProcessGPIOInterrupt, (void *)this, (void *)0, (void *)fSymIntRegister);
DLOG ("IOI2CDriveBayMGPIO::start - \"%s\" service returned: (%x)\n", fDrivebaySenseSym->getCStringNoCopy(), (int)status);
}
}
else
DLOG ("IOI2CDriveBayMGPIO::start - timeout waiting for \"%s\"\n", fDrivebaySenseSym->getCStringNoCopy());
}
else
DLOG ("IOI2CDriveBayMGPIO::start - could not create platform-drivebay-sense symbol\n");
}
else
DLOG ("IOI2CDriveBayMGPIO::start - AAPL,phandle property not found\n");
}
else
DLOG ("IOI2CDriveBayMGPIO::start - platform-drivebay-sense property not found\n");
if (status != kIOReturnSuccess)
{
DLOG ("IOI2CDriveBayMGPIO::start - PF interrupt provider not found. status=(%x)\n", (int)status);
return false;
}
fFlags |= kFlag_InterruptsRegistered;
if (false == super::start(provider))
return false;
if (kIOReturnSuccess != readI2C(k9554OutputPort, &fOutputReg, 1))
{
DLOG("IOI2CDriveBayGPIO::start -- super::start returned error\n");
freeI2CResources();
return false;
}
registerService();
timeout.tv_sec = 30;
timeout.tv_nsec = 0;
fKeyswitch = waitForService(serviceMatching("AppleKeyswitch"), &timeout);
DLOG("IOI2CDriveBayMGPIO@%lx::start succeeded\n", (fIntAddrInfo >> 8));
return true;
}
void
IOI2CDriveBayMGPIO::free(void)
{
UInt32 i;
DLOG ("IOI2CDriveBayMGPIO::free\n");
if (fDrivebaySense && fDrivebaySenseSym && fSymIntDisable && fSymIntUnRegister)
{
if (fFlags & kFlag_InterruptsEnabled)
fDrivebaySense->callPlatformFunction(fDrivebaySenseSym, FALSE, (void *)&IOI2CDriveBayMGPIO::sProcessGPIOInterrupt, (void *)this, (void *)0, (void *)fSymIntDisable);
if (fFlags & kFlag_InterruptsRegistered)
fDrivebaySense->callPlatformFunction(fDrivebaySenseSym, FALSE, (void *)&IOI2CDriveBayMGPIO::sProcessGPIOInterrupt, (void *)this, (void *)0, (void *)fSymIntUnRegister);
}
fFlags = 0;
for (i = 0; i < fClientCount; i++)
fClient[i].reg = 0x4badbeef;
if (fClientLock) { IOLockFree(fClientLock); fClientLock = NULL; }
super::free();
}
IOReturn
IOI2CDriveBayMGPIO::callPlatformFunction(
const OSSymbol *functionName,
bool waitForFunction,
void *param1,
void *param2,
void *param3,
void *param4)
{
const char *functionNameStr;
if (functionName == 0)
return kIOReturnBadArgument;
if (0 == (functionNameStr = functionName->getCStringNoCopy()))
return kIOReturnBadArgument;
if (0 == strcmp(functionNameStr, "register9554MInterruptClient"))
return registerClient((UInt32)param1, (PCA9554ClientCallback)param2, (IOService*)param3, (bool)param4);
else
if (0 == strcmp(functionNameStr, "enable9554MInterruptClient"))
return enableClient((UInt32)param1, (UInt32)param4);
else
if (0 == strcmp(functionNameStr, kSymGPIOParentIntCapable))
{
*((UInt32 *)param1) = 1;
return kIOReturnSuccess;
}
return super::callPlatformFunction(functionName, waitForFunction, param1, param2, param3, param4);
}
IOReturn
IOI2CDriveBayMGPIO::registerClient(
UInt32 reg,
PCA9554ClientCallback handler,
IOService *client,
bool isRegister)
{
IOReturn status = kIOReturnSuccess;
int i;
LOCK;
for (i = 0; i < kCLIENT_MAX; i++)
if (fClient[i].reg == reg)
break;
if (isRegister)
{
if (i >= kCLIENT_MAX) {
fClient[fClientCount].reg = reg;
fClient[fClientCount].handler = handler;
fClient[fClientCount].client = client;
fClient[fClientCount].isEnabled = false;
DLOG ("IOI2CDriveBayMGPIO::registerClient(%x) client %d added.\n", (int)reg, (int)fClientCount);
fClientCount++;
}
else
{
DLOG ("IOI2CDriveBayMGPIO::registerClient(%x) already registered.\n", (int)reg);
}
}
else {
if (i < kCLIENT_MAX)
{
fClient[i].isEnabled = false;
fClient[i].reg = 0x4badbeef;
fClient[i].client = 0;
fClient[i].handler = 0;
DLOG ("IOI2CDriveBayMGPIO::registerClient(%x) client removed.\n", (int)reg);
}
else
{
DLOG ("IOI2CDriveBayMGPIO::registerClient(%x) unregister not open\n", (int)reg);
status = kIOReturnNotOpen;
}
}
UNLOCK;
return status;
}
IOReturn
IOI2CDriveBayMGPIO::enableClient(
UInt32 reg,
bool isEnable)
{
IOReturn status = kIOReturnSuccess;
int i;
LOCK;
for (i = 0; i < kCLIENT_MAX; i++)
if (fClient[i].reg == reg)
break;
if (i < kCLIENT_MAX)
{
DLOG ("IOI2CDriveBayMGPIO::enableClient reg:%lx %s\n", reg, isEnable?"enable":"disable");
fClient[i].isEnabled = isEnable;
if (isEnable)
{
if (0 == (fFlags & kFlag_InterruptsEnabled))
{
if (kIOReturnSuccess == (status = fDrivebaySense->callPlatformFunction(fDrivebaySenseSym, FALSE, (void *)&IOI2CDriveBayMGPIO::sProcessGPIOInterrupt, (void *)this, (void *)0, (void *)fSymIntEnable)))
fFlags |= kFlag_InterruptsEnabled;
DLOG ("IOI2CDriveBayMGPIO::enableClient enable drivebay sense (%x)\n", (int)status);
}
}
else
{
if (fFlags & kFlag_InterruptsEnabled)
{
bool anyClients = 0;
for (i = 0; i < kCLIENT_MAX; i++)
{
if (fClient[i].reg != 0x4badbeef)
{
anyClients = true;
break;
}
}
if ( anyClients == false )
{
if (kIOReturnSuccess == (status = fDrivebaySense->callPlatformFunction(fDrivebaySenseSym, FALSE, (void *)&IOI2CDriveBayMGPIO::sProcessGPIOInterrupt, (void *)this, (void *)0, (void *)fSymIntEnable)))
fFlags &= ~kFlag_InterruptsEnabled;
DLOG ("IOI2CDriveBayMGPIO::enableClient disable drivebay sense (%x)\n", (int)status);
}
}
}
}
else
{
DLOG ("IOI2CDriveBayMGPIO::enableClient reg:%x not open\n", (int)reg);
status = kIOReturnBadArgument;
}
UNLOCK;
return status;
}
void
IOI2CDriveBayMGPIO::processPowerEvent(
UInt32 eventType)
{
UInt8 data;
switch (eventType)
{
case kI2CPowerEvent_OFF:
case kI2CPowerEvent_SLEEP:
if (kIOReturnSuccess == readI2C(k9554OutputPort, &data, 1))
fOutputReg = data;
break;
case kI2CPowerEvent_ON:
case kI2CPowerEvent_WAKE:
{
if (kIOReturnSuccess != writeI2C(k9554OutputPort, &fOutputReg, 1))
{
DLOG("IOI2CDriveBayGPIO::doPowerUp failed to restore outputs\n");
}
if (kIOReturnSuccess != writeI2C(k9554PolarityInv, &fPolarityReg, 1))
{
DLOG("IOI2CDriveBayGPIO::doPowerUp failed to program polarity inversion reg\n");
}
if (kIOReturnSuccess != writeI2C(k9554Config, &fConfigReg, 1))
{
DLOG("IOI2CDriveBayGPIO::doPowerUp failed to configure gpio\n");
}
break;
}
}
}
void
IOI2CDriveBayMGPIO::sProcessApplePMUInterrupt(
IOService *client,
UInt8 interruptMask,
UInt32 length,
UInt8 *buffer)
{
IOI2CDriveBayMGPIO *self;
DLOG("IOI2CDriveBayMGPIO::sProcessApplePMUInterrupt interruptMask:0x%02x length:%ld\n", interruptMask, length);
if ((interruptMask != 0x01) || (length < 5))
return;
if (0 == (self = OSDynamicCast(IOI2CDriveBayMGPIO, client)))
{
DLOG("IOI2CDriveBayMGPIO::sProcessApplePMUInterrupt unknown instance type\n");
return;
}
if (self->fIntAddrInfo == (*(UInt32 *)buffer)) self->processGPIOInterrupt(buffer[4]);
}
void
IOI2CDriveBayMGPIO::sProcessGPIOInterrupt(
IOI2CDriveBayMGPIO *self,
void *param2,
void *param3,
UInt8 newData)
{
IOReturn status;
if (0 == OSDynamicCast(IOI2CDriveBayMGPIO, self))
{
DLOG("IOI2CDriveBayMGPIO::sProcessGPIOInterrupt unknown instance type\n");
return;
}
UInt8 data;
if (kIOReturnSuccess == (status = self->readI2C(k9554InputPort, &data, 1)))
self->processGPIOInterrupt(data);
}
void
IOI2CDriveBayMGPIO::processGPIOInterrupt(
UInt8 newState)
{
UInt8 diff;
unsigned i;
if (newState == fOutputReg)
{
DLOG("IOI2CDriveBayMGPIO::processGPIOInterrupt state: 0x%02x -> 0x%02x, disregarding...\n", newState, fOutputReg);
return;
}
diff = newState ^ fOutputReg;
DLOG("IOI2CDriveBayMGPIO::processGPIOInterrupt state: 0x%02x -> 0x%02x, xor = 0x%02x\n", fOutputReg, newState, diff);
fOutputReg = newState;
if (fKeyswitch)
{
OSBoolean *locked;
locked = OSDynamicCast(OSBoolean, fKeyswitch->getProperty("Keyswitch"));
if (locked != NULL && locked->isTrue())
return;
}
for (i = 0; i < fClientCount; i++)
{
if ( (fClient[i].reg != 0x4badbeef) && (fClient[i].isEnabled) )
{
UInt32 bitOffset;
UInt8 mappedState = 0;
UInt8 mappedMask = 0;
bitOffset = (fC3Mapping) ? (i * 2) : i;
if (diff & (1 << bitOffset))
{
if (fOutputReg & (1 << bitOffset))
mappedState |= (1 << 4);
mappedMask |= (1 << 4);
}
bitOffset = (fC3Mapping) ? ((i * 2) + 1) : (i + 4);
if (diff & (1 << bitOffset))
{
if (fOutputReg & (1 << bitOffset))
mappedState |= (1 << 3);
mappedMask |= (1 << 3);
}
if (mappedMask)
{
DLOG("IOI2CDriveBayMGPIO::processGPIOInterrupt call client %d mask:0x%02x state:0x%02x\n", i, mappedMask, mappedState);
fClient[i].handler(fClient[i].client, mappedMask, mappedState);
}
}
}
}