IOI2CDriveBayGPIO.cpp [plain text]
#include <IOKit/IOLib.h>
#include <IOKit/IODeviceTreeSupport.h>
#include "IOI2CDriveBayGPIO.h"
#ifdef DLOG
#undef DLOG
#endif
#ifdef IOI2CDriveBayGPIO_DEBUG
#define DLOG(fmt, args...) kprintf(fmt, ## args)
#else
#define DLOG(fmt, args...)
#endif
#define super IOI2CDevice
OSDefineMetaClassAndStructors(IOI2CDriveBayGPIO, IOI2CDevice)
bool IOI2CDriveBayGPIO::start(IOService *provider)
{
IOReturn status;
OSData *data;
UInt32 regProperty, index, length;
UInt32 *quadlet;
bool found;
IOService *service;
mach_timespec_t timeout;
DLOG("IOI2CDriveBayGPIO::start\n");
if (0 == (data = OSDynamicCast(OSData, provider->getProperty("reg"))))
{
DLOG("IOI2CDriveBayGPIO::start -- no reg property\n");
return false;
}
regProperty = *((UInt32 *)data->getBytesNoCopy());
regProperty <<= 8;
timeout.tv_sec = 30;
timeout.tv_nsec = 0;
if (0 == (fPCA9554M = waitForService(serviceMatching("IOI2CDriveBayMGPIO"), &timeout)))
{
DLOG("IOI2CDriveBayGPIO::start -- timeout waiting for IOI2CDriveBayMGPIO\n");
return false;
}
if (0 == (service = OSDynamicCast(IOService, fPCA9554M->getParentEntry(gIOServicePlane))))
{
DLOG("IOI2CDriveBayGPIO::start -- no 9554M parent\n");
return false;
}
if (0 == (data = OSDynamicCast(OSData, service->getProperty(kI2CGPIOCombined))))
{
DLOG("IOI2CDriveBayGPIO::start -- no 9554M combined property\n");
return false;
}
length = data->getLength() / sizeof(UInt32);
quadlet = (UInt32 *)data->getBytesNoCopy();
found = false;
for (index = 0; index < length; index++)
{
if (regProperty == *quadlet)
{
fBayIndex = index;
found = true;
DLOG("IOI2CDriveBayGPIO::start we are: 0x%08x fBayIndex = %u\n", regProperty, fBayIndex);
break;
}
quadlet++;
}
if (!found)
{
DLOG("IOI2CDriveBayGPIO %x couldn't find combined gpio node, bailing...\n", regProperty);
return false;
}
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("IOI2CDriveBayGPIO@%lx::start fConfigReg = %02x fPolarityReg = %02x\n", regProperty, fConfigReg, fPolarityReg);
fClientLock = IOLockAlloc();
DLOG("IOI2CDriveBayGPIO::start - register9554MInterruptClient %u\n", fBayIndex);
if (kIOReturnSuccess != (status = fPCA9554M->callPlatformFunction("register9554MInterruptClient", false,
(void *)fBayIndex, (void *)&sProcess9554MInterrupt, (void *)this, (void *)true)))
{
DLOG("IOI2CDriveBayGPIO::start failed to register\n");
return false;
}
if (false == super::start(provider))
{
DLOG("IOI2CDriveBayGPIO::start -- super::start returned error\n");
return false;
}
publishChildren(provider);
registerService();
DLOG("IOI2CDriveBayGPIO@%lx::start succeeded\n", getI2CAddress());
return true;
}
void IOI2CDriveBayGPIO::stop(IOService *provider)
{
UInt32 i;
DLOG("IOI2CDriveBayGPIO::stop\n");
for (i = 0; i < kNumGPIOs; i++)
{
if (fClient[i] != 0)
{
IOFree(fClient[i], sizeof(I2CGPIOCallbackInfo));
fClient[i] = 0;
}
}
fPCA9554M->callPlatformFunction("register9554MInterruptClient", false,
(void *)fBayIndex, (void *)&sProcess9554MInterrupt, (void *)this, (void *)false);
if (fClientLock) { IOLockFree(fClientLock); fClientLock = 0; }
super::stop(provider);
}
void IOI2CDriveBayGPIO::free(void)
{
DLOG("IOI2CDriveBayGPIO::free\n");
super::free();
}
IOReturn IOI2CDriveBayGPIO::publishChildren(IOService *provider)
{
IOReturn status = kIOReturnSuccess;
OSIterator *iter;
IORegistryEntry *next;
IOService *nub;
if (iter = provider->getChildIterator(gIODTPlane))
{
while (next = (IORegistryEntry *)(iter->getNextObject()))
{
nub = OSDynamicCast(IOService, OSMetaClass::allocClassWithName("IOI2CService"));
if (nub)
{
nub->init(next, gIODTPlane);
nub->attach(this);
nub->registerService();
}
}
iter->release();
}
return status;
}
IOReturn IOI2CDriveBayGPIO::callPlatformFunction(
const OSSymbol *functionName,
bool waitForFunction,
void *param1, void *param2,
void *param3, void *param4)
{
const char *functionNameStr;
UInt8 data;
IOReturn status;
if (functionName == 0)
return kIOReturnBadArgument;
functionNameStr = functionName->getCStringNoCopy();
if (0 == strcmp(functionNameStr, kSymGPIOParentWriteGPIO))
{
return readModifyWriteI2C(k9554OutputPort, (UInt8)(UInt32)param2, (UInt8)(UInt32)param3);
}
else
if (0 == strcmp(functionNameStr, kSymGPIOParentReadGPIO))
{
if (kIOReturnSuccess == (status = readI2C(k9554InputPort, &data, 1)))
*(UInt32 *)param2 = (UInt32)data;
return status;
}
else if (0 == strcmp(functionNameStr, kSymGPIOParentRegister))
return registerGPIOClient((UInt32)param1, (GPIOEventHandler)param3, (IOService*)param4, true);
else
if (0 == strcmp(functionNameStr, kSymGPIOParentUnregister))
return registerGPIOClient((UInt32)param1, (GPIOEventHandler)0, (IOService*)0, false);
else
if (0 == strcmp(functionNameStr, kSymGPIOParentEvtEnable))
return enableGPIOClient((UInt32)param1, true);
else
if (0 == strcmp(functionNameStr, kSymGPIOParentEvtDisable))
return enableGPIOClient((UInt32)param1, false);
else
if (0 == strcmp(functionNameStr, kSymGPIOParentIntCapable))
{
*((UInt32 *)param1) = 1;
return kIOReturnSuccess;
}
return super::callPlatformFunction(functionName, waitForFunction, param1, param2, param3, param4);
}
IOReturn
IOI2CDriveBayGPIO::readModifyWriteI2C(
UInt8 subAddr,
UInt8 value,
UInt8 mask)
{
IOReturn status;
UInt32 clientKey;
UInt8 data;
if (kIOReturnSuccess == (status = lockI2CBus(&clientKey)))
{
if (kIOReturnSuccess != (status = readI2C(subAddr, &data, 1, clientKey)))
{
DLOG("IOI2CDriveBayGPIO@%lx::rmw read S:0x%x error: 0x%08x\n", getI2CAddress(), subAddr, status);
}
else
{
data = ((data & ~mask) | (value & mask));
if (kIOReturnSuccess != (status = writeI2C(subAddr, &data, 1, clientKey)))
{
DLOG("IOI2CDriveBayGPIO@%lx::rmw write S:0x%x error: 0x%08x\n", getI2CAddress(), subAddr, status);
}
}
unlockI2CBus(clientKey);
}
return status;
}
IOReturn
IOI2CDriveBayGPIO::registerGPIOClient(
UInt32 id,
GPIOEventHandler handler,
IOService *client,
bool isRegister)
{
I2CGPIOCallbackInfo *clientInfo;
if (id >= kNumGPIOs)
return kIOReturnBadArgument;
if (isRegister)
{
if (fClient[id] != 0)
{
DLOG ("IOI2CDriveBayGPIO@%lx::registerGPIOClient id:%d already open\n", getI2CAddress(), id);
return kIOReturnStillOpen;
}
if (0 == (clientInfo = (I2CGPIOCallbackInfo *)IOMalloc(sizeof(I2CGPIOCallbackInfo))))
{
DLOG ("IOI2CDriveBayGPIO@%lx::registerGPIOClient id:%d malloc err\n", getI2CAddress(), id);
return kIOReturnNoMemory;
}
clientInfo->handler = handler;
clientInfo->self = client;
clientInfo->isEnabled = false;
fClient[id] = clientInfo;
}
else {
if (fClient[id] == 0)
{
DLOG ("IOI2CDriveBayGPIO@%lx::registerGPIOClient - unregister id:%d not open\n", getI2CAddress(), id);
return kIOReturnNotOpen;
}
clientInfo = fClient[id];
fClient[id] = 0;
}
return kIOReturnSuccess;
}
IOReturn
IOI2CDriveBayGPIO::enableGPIOClient(
UInt32 id,
bool isEnable)
{
if ((id >= kNumGPIOs) || (fClient[id] == 0))
{
DLOG("IOI2CDriveBayGPIO@%lx::enableClient - error %d not registered\n", getI2CAddress(), id);
return kIOReturnBadArgument;
}
if (fClient[id]->isEnabled == isEnable)
return kIOReturnSuccess;
IOLockLock(fClientLock);
fClient[id]->isEnabled = isEnable;
if (isEnable)
{
if (fClientsEnabled == 0) fPCA9554M->callPlatformFunction("enable9554MInterruptClient", false, (void *)fBayIndex, (void *)0, (void *)0, (void *)true);
fClientsEnabled |= (1 << id);
}
else {
fClientsEnabled &= ~(1 << id);
if (fClientsEnabled == 0) fPCA9554M->callPlatformFunction("enable9554MInterruptClient", false, (void *)fBayIndex, (void *)0, (void *)0, (void *)false);
}
IOLockUnlock(fClientLock);
DLOG("IOI2CDriveBayGPIO@%lx::enableGPIOClient %s client %d\n", getI2CAddress(), isEnable?"enable":"disable", id);
return kIOReturnSuccess;
}
void
IOI2CDriveBayGPIO::sProcess9554MInterrupt(
IOI2CDriveBayGPIO *self,
UInt8 eventMask,
UInt8 newState)
{
if (self)
self->process9554MInterrupt(eventMask, newState);
}
void
IOI2CDriveBayGPIO::process9554MInterrupt(
UInt8 eventMask,
UInt8 newState)
{
UInt8 value;
if (eventMask & (1 << kSwitch))
{
DLOG("IOI2CDriveBayGPIO 0x%02x got switch event\n", getI2CAddress());
if (fClient[kSwitch] && fClient[kSwitch]->isEnabled)
{
value = ((newState >> kSwitch) & 1);
value ^= 0x1; fClient[kSwitch]->handler((void *)fClient[kSwitch]->self, (void *)(UInt32)value, 0, 0);
}
}
if (eventMask & (1 << kPresent))
{
DLOG("IOI2CDriveBayGPIO 0x%02x got insertion/removal event\n", getI2CAddress());
if (fClient[kPresent] && fClient[kPresent]->isEnabled)
{
value = ((newState >> kPresent) & 1);
value ^= 0x1; fClient[kPresent]->handler((void *)fClient[kPresent]->self, (void *)(UInt32)value, 0, 0);
}
}
}
void
IOI2CDriveBayGPIO::processPowerEvent(
UInt32 eventType)
{
}
void IOI2CDriveBayGPIO::doPowerDown(void)
{
DLOG("IOI2CDriveBayGPIO::doPowerDown %x\n", getI2CAddress());
if (kIOReturnSuccess != readI2C(k9554OutputPort, &fOutputReg, 1))
{
fOutputReg = 0x00;
DLOG("IOI2CDriveBayGPIO::doPowerDown failed to save output state\n");
}
}
void IOI2CDriveBayGPIO::doPowerUp(void)
{
DLOG("IOI2CDriveBayGPIO::doPowerUp %x\n", getI2CAddress());
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");
}
}