/* * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. * * File: $Id: IOI2CControllerPPC.cpp,v 1.12 2006/02/17 22:17:40 hpanther Exp $ * * DRI: Joseph Lehrer * * $Log: IOI2CControllerPPC.cpp,v $ * Revision 1.12 2006/02/17 22:17:40 hpanther * Fix * * 4436981 IOI2CControllerPPC.kext: Replace semaphore with timeout semaphore block CPU Software IOI2C (2) Analyze McKenna, Mike leopard 2 2 Crash or Data Loss Today 8:00 AM * * Revision 1.11 2006/02/02 22:30:01 hpanther * Repair additional race condition. * * Revision 1.10 2006/02/02 00:24:46 hpanther * Replace flawed IOLock synchronization with semaphores. * A bit of cleanup on the logging side. * * Revision 1.9 2005/09/08 03:17:16 galcher * [rdar://4242471] Remove Kodiak Workarounds (IOI2CControllerPPC). * Removed variables and kodiakSyncWait() inline definition. Removed setup of * kodiakSyncWait in ::start. Removed usage of kodiakSyncWait() in ::writeReg() * and ::readReg(). * * Revision 1.8 2005/05/31 23:27:37 tmason * Fix for Radar bug ID 4117978, panic with boot-args lcks=1 * * Revision 1.7 2005/05/16 23:30:57 galcher * [rdar://problem/4120572] OS changes needed to support Kodiak <= 1.1. * Add kodiakSyncWait() routine and use it in the NorthBridge I2C register * accessors. NOTE - this will need to be removed before shipping. * * Revision 1.6 2005/02/24 22:07:00 jlehrer * [4022756] Fixed fMODE_CLKDIV enums. * * Revision 1.5 2004/12/17 04:53:15 jlehrer * [3925249] Fix polled mode timeout warning. * * Revision 1.4 2004/12/17 00:40:47 jlehrer * [3915595] Allow at least 5 seconds before timeout for devices with long clock stretching after wake from sleep. * Client driver can specify a longer timeout in IOI2CCommand.timeout_uS param. * Replaced semaphore with an IOLock. * * Revision 1.3 2004/12/15 02:39:41 jlehrer * [3867728] Fix problem with address no-ack on write transactions. * [3893026] Return specific I2C transaction errors. * * Revision 1.2 2004/09/17 21:56:27 jlehrer * Removed ASPL headers. * Added workaround for u3 DP timebase synchronization: * On wake from sleep MR4CPU timebase sync happens before PM threads start. * We need to be online and ready to process the i2c transactions. * This is enabled by the PE by setting the "AAPL,i2c-no-sleep" property in our provider. * Added publish IOResource for uni-n and mac-io instance. * Removed unused test code fragments. * * Revision 1.1 2004/06/07 21:53:41 jlehrer * Initial Checkin * * */ #include #include #include #include "IOI2CController.h" #include #include #include #ifdef DEBUG #define PPC_I2C_DEBUG 1 #endif #if (defined(PPC_I2C_DEBUG) && PPC_I2C_DEBUG) #define DLOG(fmt, args...) FireLog(fmt, ## args) #else #define DLOG(fmt, args...) #endif #ifdef DEBUG #define I2C_ERRLOG 1 #endif #if (defined(I2C_ERRLOG) && I2C_ERRLOG) #define ERRLOG(fmt, args...) FireLog(fmt, ## args) #else #define ERRLOG(fmt, args...) #endif extern "C" vm_offset_t ml_io_map(vm_offset_t phys_addr, vm_size_t size); #if (defined(PPC_I2C_DEBUG) && PPC_I2C_DEBUG) void TLOG(const char *str) { AbsoluteTime abst; uint64_t t; clock_get_uptime(&abst); absolutetime_to_nanoseconds(abst, &t); DLOG("%s @%d.%u\n", str?str:"", (int)(t/1000000ULL), (int)(t%1000000ULL)); } #else #define TLOG(s) #endif class IOI2CControllerPPC : public IOI2CController { OSDeclareDefaultStructors(IOI2CControllerPPC) public: virtual bool start(IOService *provider); virtual void stop(IOService *provider); virtual void free(void); protected: // Subclass required methods... virtual IOReturn processLockI2CBus( UInt32 bus); virtual IOReturn processUnlockI2CBus( UInt32 bus); virtual IOReturn processReadI2CBus( IOI2CCommand *cmd); virtual IOReturn processWriteI2CBus( IOI2CCommand *cmd); private: bool fU3NoSleep; virtual IOReturn setPowerState( unsigned long newPowerState, IOService *dontCare); enum { iMODE = 0x0, // MODE Read/Write Mode Register iCNTRL = 0x1, // CNTRL Read/Write Control Register iSTATUS = 0x2, // STATUS Read Only Status Register iISR = 0x3, // ISR Read/Write Interrupt Status Register iIER = 0x4, // IER Read/Write Interrupt Enable Register iADDR = 0x5, // ADDR Read/Write Address Register iSUBADDR = 0x6, // SUBADDR Read/Write Sub-address Register iDATA = 0x7, // DATA Read/Write IIC Data Transmit/Receive Register iREVNUM = 0x8, // REVNUM Read Only Revision Number Register iRISETIMECNT = 0x9, // RISETIMECNT Read/Write Clock Rise Time Counter value iBITTIMECNT = 0xa, // BITTIMECNT Read/Write IIC Bit Time Counter value kIIC_REG_COUNT = 0xb // Number of IIC cell registers. }; enum { fMODE_PORTSEL = (1 << 4), // PORTSEL IIC Port Select fMODE_APMODE = (3 << 2), // APMODE Address Phase Mode, bits[2:3] fMODE_APMODE_dumb = (0 << 2), fMODE_APMODE_std = (1 << 2), fMODE_APMODE_sub = (2 << 2), fMODE_APMODE_comb = (3 << 2), fMODE_CLKDIV = (3 << 0), // N Clock divider N, bits[0:1] fMODE_CLKDIV_100kHz = (0 << 0), fMODE_CLKDIV_50kHz = (1 << 0), fMODE_CLKDIV_25kHz = (2 << 0), }; enum { fCNTRL_START = (1 << 3), // START Send Start Condition fCNTRL_STOP = (1 << 2), // STOP Send Stop Condition fCNTRL_XADDR = (1 << 1), // XADDR Send Address Phase fCNTRL_AAK = (1 << 0), // AAK Assert Acknowledge fCNTRL_NoAAK = (0 << 0), // NoAAK Assert Acknowledge }; enum { fSTATUS_ISCL = (1 << 4), // ISCL Double rank synchronized ISCL of active port fSTATUS_ISDA = (1 << 3), // ISDA Double rank synchronized ISDA of active port fSTATUS_LASTRW = (1 << 2), // LASTRW_ Last R/W_ fSTATUS_LASTAAK = (1 << 1), // LASTAAK Last Acknowledge fSTATUS_BUSY = (1 << 0), // BUSY Bus Busy Indicator }; enum { fISR_ISTART = (1 << 3), // ISTART Start Condition Sent Interrupt fISR_ISTOP = (1 << 2), // ISTOP Stop Condition Sent Interrupt fISR_IADDR = (1 << 1), // IADDR Address Phase Sent Interrupt fISR_IDATA = (1 << 0), // IDATA Data Byte Sent or Received Interrupt fISR_IMASK = (fISR_ISTART | fISR_ISTOP | fISR_IADDR | fISR_IDATA), }; enum { fIER_ESTART = (1 << 3), // ESTART Enable Start Condition Sent Interrupt fIER_ESTOP = (1 << 2), // ESTOP Enable Stop Condition Sent Interrupt fIER_EADDR = (1 << 1), // EADDR Enable Address Phase Sent Interrupt fIER_EDATA = (1 << 0), // EDATA Enable Data Byte Sent or Received Interrupt }; enum { fADDR_MASK = (0xFE), // ADDR[1:7] Master Address bits[0:6] fADDR_ReadWrite = (1 << 0), // ReadWrite_ R/W_ bit fADDR_WRITE = (0 << 0), fADDR_READ = (1 << 0), }; private: volatile UInt8 *iic[kIIC_REG_COUNT]; bool fInterruptCapable; bool fInterruptRegistered; int cellID; void writeReg( int reg_index, UInt8 value); UInt8 readReg( int reg_index); IOReturn i2cTransaction( IOI2CCommand *cmd, bool isRead); static void sProcessInterrupt( OSObject *target, void *refCon, IOService *nub, int source); void processInterrupt(void); UInt8 *i2c_data; int i2c_index; int i2c_count; volatile UInt32 i2c_xfer; bool i2c_readDirection; UInt32 i2c_rate; IOReturn i2c_status; UInt32 i2c_state; semaphore_t i2c_sema; }; #define super IOI2CController OSDefineMetaClassAndStructors( IOI2CControllerPPC, IOI2CController ) bool IOI2CControllerPPC::start( IOService *provider) { // IOReturn status; OSData *t; UInt32 baseAddress; UInt32 steps; UInt8 *base; char *resourceName = 0; DLOG("+IOI2CControllerPPC::start\n"); if (0 == (t = OSDynamicCast(OSData, provider->getProperty("AAPL,address")))) return false; if (0 == (baseAddress = *((UInt32*)t->getBytesNoCopy()))) return false; if (0 == (base = (UInt8 *)ml_io_map(baseAddress, 0x1000))) return false; if (0 == (t = OSDynamicCast(OSData, provider->getProperty("AAPL,address-step")))) return false; steps = *((UInt32*)t->getBytesNoCopy()); if (0 == (t = OSDynamicCast(OSData, provider->getProperty("AAPL,i2c-rate")))) return false; i2c_rate = *((UInt32*)t->getBytesNoCopy()); if (0 == (t = OSDynamicCast(OSData, provider->getProperty("interrupts")))) fInterruptCapable = FALSE; else fInterruptCapable = TRUE; semaphore_create(current_task(), &i2c_sema, SYNC_POLICY_FIFO, 0); if(0==i2c_sema) return false; DLOG("IOI2CControllerPPC::start base:%08lx steps:%ld\n", (UInt32)base, steps); iic[iMODE] = base + (steps * iMODE); // Configure the transmission mode of the i2c cell and the databit rate. iic[iCNTRL] = base + (steps * iCNTRL); // Holds the 4 bits used to start the operations on the i2c interface. iic[iSTATUS] = base + (steps * iSTATUS); // Status bits for the i2 cell and the i2c interface. iic[iISR] = base + (steps * iISR); // Holds the status bits for the interrupt conditions. iic[iIER] = base + (steps * iIER); // Eneables the bits that allow the four interrupt status conditions. iic[iADDR] = base + (steps * iADDR); // Holds the 7 bits address and the R/W bit. iic[iSUBADDR] = base + (steps * iSUBADDR); // the 8bit subaddress.. iic[iDATA] = base + (steps * iDATA); // from where we read and write data writeReg(iIER, 0x00); writeReg(iISR, 0x00); DLOG("IOI2CControllerPPC::start publishResource...\n"); if (t = OSDynamicCast(OSData, provider->getProperty("compatible"))) { const char *cstr = (const char *)t->getBytesNoCopy(); if (cstr) while (*cstr) { if (0 == strncmp("uni-n-i2c-control", cstr, strlen("uni-n-i2c-control"))) { if (provider->getProperty("AAPL,i2c-no-sleep")) fU3NoSleep = true; cellID = 1; resourceName = "IOI2CControllerPPC.uni-n"; break; } else if (0 == strncmp("mac-io-i2c-control", cstr, strlen("mac-io-i2c-control"))) { fDisablePowerManagement = true; cellID = 2; resourceName = "IOI2CControllerPPC.mac-io"; break; } cstr += (strlen(cstr) + 1); } if (resourceName == 0) DLOG("IOI2CControllerPPC::start publishResource NO MATCH\n"); } else DLOG("IOI2CControllerPPC::start publishResource compatible property not found\n"); if (false == super::start(provider)) { DLOG("-IOI2CControllerPPC::start super::start failed\n"); return false; } registerService(); publishChildren(); if (resourceName) { publishResource(resourceName, this); DLOG("IOI2CControllerPPC::start publishResource %s\n", resourceName); } DLOG("-IOI2CControllerPPC::start\n"); return true; } void IOI2CControllerPPC::stop(IOService * provider) { DLOG("IOI2CControllerPPC::stop\n"); if (fInterruptRegistered) { fInterruptCapable = FALSE; provider->disableInterrupt(0); provider->unregisterInterrupt(0); } super::stop(provider); } void IOI2CControllerPPC::free( void ) { if(i2c_sema) semaphore_destroy(current_task(), i2c_sema); super::free(); } // Subclass required methods... IOReturn IOI2CControllerPPC::processLockI2CBus( UInt32 bus) { return kIOReturnSuccess; // Just acknowledge. The superclass handles locking. } IOReturn IOI2CControllerPPC::processUnlockI2CBus( UInt32 bus) { return kIOReturnSuccess; // Just acknowledge. The superclass handles unlocking. } IOReturn IOI2CControllerPPC::processReadI2CBus( IOI2CCommand *cmd) { IOReturn status; if (cmd == NULL) return kIOReturnBadArgument; cmd->bytesTransfered = 0; status = i2cTransaction(cmd, true); if (status == kIOReturnTimeout) ERRLOG("IOI2CControllerPPC::i2cRead timed out\n"); else if (status != kIOReturnSuccess) ERRLOG("IOI2CControllerPPC::i2cRead error: %x\n", status); return status; } IOReturn IOI2CControllerPPC::processWriteI2CBus( IOI2CCommand *cmd) { IOReturn status; if (cmd == NULL) return kIOReturnBadArgument; cmd->bytesTransfered = 0; status = i2cTransaction(cmd, false); if (status == kIOReturnTimeout) ERRLOG("IOI2CControllerPPC::i2cWrite timed out\n"); else if (status != kIOReturnSuccess) ERRLOG("IOI2CControllerPPC::i2cWrite error: %x\n", status); return status; } // ******************************************************************* // I2C Read / Write Interface Methods // ******************************************************************* IOReturn IOI2CControllerPPC::i2cTransaction( IOI2CCommand *cmd, bool isRead) { IOReturn status; kern_return_t rval = 0; int retry; bool intMode; AbsoluteTime deadline; UInt8 mode = cmd->mode; UInt8 address = cmd->address; UInt8 subAddress = cmd->subAddress; UInt32 timeout_uS = cmd->timeout_uS; // By default we allow up to 5 seconds for a transaction to complete. if (timeout_uS < 5000000) // that's the minimum timeout the caller can request. timeout_uS = 5000000; clock_interval_to_deadline(timeout_uS, kMicrosecondScale, &deadline); if (isRead) address |= fADDR_READ; else address &= ~fADDR_READ; // Translate IOI2C mode to PPCI2C mode... switch (mode) { default: case kI2CMode_Unspecified: return kIOReturnUnsupported; // mode = fMODE_APMODE_dumb; break; case kI2CMode_Standard: mode = fMODE_APMODE_std; break; case kI2CMode_StandardSub: mode = fMODE_APMODE_sub; break; case kI2CMode_Combined: mode = fMODE_APMODE_comb; break; } switch (cmd->bus) // bus can be either {0 or 1} only! { case 0: break; case 1: mode |= fMODE_PORTSEL; break; default: ERRLOG("-i2cTransaction invalid bus:%ld\n", cmd->bus); return kIOReturnBadArgument; } if (i2c_rate < 50) mode |= fMODE_CLKDIV_25kHz; else if (i2c_rate < 100) mode |= fMODE_CLKDIV_50kHz; else mode |= fMODE_CLKDIV_100kHz; // Wait up to 1 second for busy=0 before writing mode reg... for (retry = 1000; (retry > 0) && (readReg(iSTATUS) & fSTATUS_BUSY); retry--) { if (ml_at_interrupt_context()) IODelay(1000); else IOSleep(1); } if (retry <= 0) { ERRLOG("-IOI2CControllerPPC::i2cTransaction IIC cell busy.\n"); return kIOReturnDeviceError; // 0x2e9 } // Ready to go... writeReg(iIER, 0); // disable all interrupts writeReg(iISR, fISR_IMASK); // clear pending interrupts i2c_xfer = true; // set transfer in progress flag i2c_state = 0; i2c_status = kIOReturnSuccess; i2c_data = cmd->buffer; i2c_count = cmd->count; i2c_index = 0; i2c_readDirection = isRead; // Check conditions for an interrupt transaction. intMode = ( fInterruptCapable && (false == ml_at_interrupt_context()) && (0 == (cmd->options & kI2COption_NoInterrupts)) ); if (intMode) { if (fInterruptRegistered == false) { DLOG("IOI2CControllerPPC::i2cTransaction calling registerInterrupt\n"); if (kIOReturnSuccess != (status = fProvider->registerInterrupt(0, this, sProcessInterrupt, 0))) { ERRLOG("-IOI2CControllerPPC::i2cTransaction registerInterrupt returned:0x%lx\n", status); return status; } fInterruptRegistered = true; } // DLOG("IOI2CPPC imode\n"); if (kIOReturnSuccess != (status = fProvider->enableInterrupt(0))) { ERRLOG("-IOI2CControllerPPC::i2cTransaction enableInterrupt failed: 0x%08x\n", status); return status; } } else i2c_state |= 0x80000000; writeReg(iMODE, mode); writeReg(iADDR, address); // if ((mode == fMODE_APMODE_sub) || (mode == fMODE_APMODE_comb)) writeReg(iSUBADDR, subAddress); writeReg(iISR, fISR_IMASK); // clear pending interrupts writeReg(iIER, fISR_IMASK); // enable all interrupts writeReg(iCNTRL, fCNTRL_XADDR); // start the bus transaction if (intMode == false) { // DLOG("IOI2CPPC pmode\n"); while (i2c_xfer) { if (readReg(iISR)) processInterrupt(); else { if (ml_at_interrupt_context()) IODelay(1000); else IOSleep(1); } } } else { mach_timespec_t timeout = { 5, 0 }; // 5 secs rval = semaphore_timedwait(i2c_sema, timeout); DLOG("[%p] woke from semaphore, i2c_state = %x\n", this, i2c_state); } // Clear transfer in progress flag to try preventing the interupt context from calling IOLockWake after a timeout. // If it does.. no big deal. i2c_xfer = false; // Wait for IIC cell to clear its busy bit before the next transaction... // This indicates the stop bit was sent and the SCL and SDA lines have deasserted. UInt8 reg = readReg(iSTATUS); if (reg & fSTATUS_BUSY) { for (retry = 1000; retry > 0; retry--) { reg = readReg(iSTATUS); if ((reg & fSTATUS_BUSY) == 0) break; if (ml_at_interrupt_context()) IODelay(1000); else IOSleep(1); } if (retry == 0) ERRLOG("IOI2CControllerPPC::i2cTransaction IIC cell got no stop and still busy: 0x%02x\n", reg); if (retry < 998) ERRLOG("IOI2CControllerPPC::i2cTransaction Waited %d ms for IIC Cell to complete:\n", 1000-retry); } // This is the end of the transaction. writeReg(iIER, 0); // disable all interrupts writeReg(iISR, fISR_IMASK); // clear pending interrupts if (intMode) { if (kIOReturnSuccess != (status = fProvider->disableInterrupt(0))) { ERRLOG("IOI2CControllerPPC::i2cTransaction disableInterrupt failed: 0x%08x\n", status); } } // Evaluate transaction status if (i2c_status == kIOReturnSuccess) { // If transaction successful and didn't timeout? if (rval == KERN_OPERATION_TIMED_OUT) { ERRLOG("IOI2CControllerPPC::i2c%c timed-out B:0x%02x A:0x%02x %d/%d i2c_state:0x%08x\n", i2c_readDirection?'R':'W', cmd->bus, address, i2c_index, i2c_count, i2c_state); // Only indicate a timeout if the stop still has not occured. if (0 == (i2c_state & 0x1000100)) i2c_status = kIOReturnTimeout; // 0x2d6 } } else // kIOReturnAborted or kIOReturnNotResponding { // kIOReturnAborted is returned only for write transactions during the data phase. // It means the slave device no-acked a data byte. // kIOReturnNotResponding is returned for address phase no-acks. ERRLOG("i2c%c B:0x%02x A:0x%02x %s: %d/%d i2c_state:0x%08x\n", i2c_readDirection?'R':'W', cmd->bus, address, (i2c_status == kIOReturnAborted)?"aborted":(i2c_status == kIOReturnNotResponding)?"not responding":"???", i2c_index, i2c_count, i2c_state); } // Return the actual number of bytes transfered. cmd->bytesTransfered = i2c_index; // kprintf("-i2cTransaction **** done\n"); return i2c_status; } // ******************************************************************* // interrupt processing // ******************************************************************* #define iLOG(fmt, args...) //#define iLOG DLOG void IOI2CControllerPPC::sProcessInterrupt( OSObject *target, void *refCon, IOService *nub, int source) { IOI2CControllerPPC *self = OSDynamicCast(IOI2CControllerPPC, target); if (self) self->processInterrupt(); } void IOI2CControllerPPC::processInterrupt(void) { register UInt8 byte; register UInt8 isrReg; register UInt8 statusReg; isrReg = readReg(iISR); DLOG("+ [%p] IOI2CControllerPPC::processInterrupt iISR=%02x\n", this, isrReg); if (i2c_readDirection) { if (isrReg & fISR_IADDR) { ERRLOG("[%p] i2cR Addr\n", this); i2c_state |= 0x01; statusReg = readReg(iSTATUS); // read bus status if (statusReg & fSTATUS_LASTAAK) // got an ack? { if (i2c_count > 1) // more than one byte? { iLOG(" ->aak\n"); i2c_state |= 0x02; writeReg(iCNTRL, fCNTRL_AAK); // ..signal ack control } else // zero or one bytes? { iLOG(" ->nak\n"); i2c_state |= 0x04; writeReg(iCNTRL, fCNTRL_NoAAK); // ..signal noack control } } else // got noack? { iLOG(" got nak\n"); i2c_status = kIOReturnNotResponding; i2c_state |= 0x08; // writeReg(iCNTRL, fCNTRL_STOP); // stop is automatically sent after nak } writeReg(iISR, fISR_IADDR); // clear interrupt } if (isrReg & fISR_IDATA) { iLOG("i2cR Data"); if (i2c_count) { byte = readReg(iDATA); // save next data byte i2c_data[i2c_index++] = byte; iLOG(" %d/%d=0x%02x",i2c_index,i2c_count,byte); i2c_state |= 0x10; } if (i2c_index < i2c_count) // more bytes? { if (i2c_index >= (i2c_count - 1)) // is next byte the last byte? { iLOG(" ->nak\n"); writeReg(iCNTRL, fCNTRL_NoAAK); // signal noack control i2c_state |= 0x20; } else { iLOG(" ->aak\n"); writeReg(iCNTRL, fCNTRL_AAK); // signal ack control i2c_state |= 0x40; } } else { iLOG(" ->stop\n"); i2c_state |= 0x80; } writeReg(iISR, fISR_IDATA); // clear interrupt } if (isrReg & fISR_ISTOP) { ERRLOG("[%p] i2cR Stop\n", this); // writeReg(iIER, 0); // disable all interrupts writeReg(iISR, fISR_ISTOP); // clear stop interrupt i2c_state |= 0x100; if (i2c_xfer) { i2c_xfer = false; // clear transfer in progress flag i2c_state |= 0x200; } else { ERRLOG("i2cR Stop not i2c_xfer\n"); } if (0 == (i2c_state & 0x80000000)) // wakeup sleeping i2cTransaction thread if not in polled mode. { kern_return_t result=semaphore_signal(i2c_sema); DLOG(" [%p] processInterrupt: signalled semaphore, result %08x\n", this, result); } else { ERRLOG("i2cR Stop - synchronous, not signalling\n"); } } } else // write direction { if (isrReg & fISR_IADDR) { ERRLOG("[%p] i2cW Addr\n", this); i2c_state |= 0x10000; statusReg = readReg(iSTATUS); // read bus status if (statusReg & fSTATUS_LASTAAK) // got an ack? { byte = i2c_data[i2c_index++]; writeReg(iDATA, byte); // load first data byte iLOG(" ack -> %d/%d=0x%02x\n",i2c_index,i2c_count,byte); i2c_state |= 0x20000; } else { iLOG(" got nak\n"); i2c_status = kIOReturnNotResponding; i2c_state |= 0x80000; } writeReg(iISR, fISR_IADDR); // clear interrupt } if (isrReg & fISR_IDATA) { iLOG("i2cW Data"); statusReg = readReg(iSTATUS); // read bus status if (statusReg & fSTATUS_LASTAAK) // got an ack? { if (i2c_index < i2c_count) // more bytes? { byte = i2c_data[i2c_index++]; writeReg(iDATA, byte); // load next data byte iLOG(" -> %d/%d=0x%02x\n",i2c_index,i2c_count,byte); i2c_state |= 0x100000; } else // last byte? { iLOG(" ->stop\n"); writeReg(iCNTRL, fCNTRL_STOP); // signal stop control i2c_state |= 0x200000; } } else { i2c_status = kIOReturnAborted; iLOG(" got nak\n"); i2c_state |= 0x400000; } writeReg(iISR, fISR_IDATA); // clear data interrupt } if (isrReg & fISR_ISTOP) { ERRLOG("[%p] i2cW Stop\n", this); writeReg(iISR, fISR_ISTOP); // clear stop interrupt // writeReg(iIER, 0); // disable all interrupts i2c_state |= 0x1000000; if (i2c_xfer) { i2c_xfer = false; // clear transfer in progress flag i2c_state |= 0x2000000; } else { ERRLOG("i2cW Stop NOT i2c_xfer\n"); } if (0 == (i2c_state & 0x80000000)) // wakeup sleeping i2cTransaction thread if not in polled mode. { kern_return_t result=semaphore_signal(i2c_sema); DLOG(" [%p] processInterrupt: signalled semaphore, result %08x\n", this, result); } else { ERRLOG("i2cW Stop - synchronous, not signalling\n"); } } } ERRLOG("- IOI2CControllerPPC::processInterrupt\n"); } // ******************************************************************* // IIC Cell Hardware I/O // ******************************************************************* /* static const char *vstrs[]= {"iMODE","iCNTRL","iSTATUS","iISR","iIER","iADDR","iSUBADDR","iDATA","iREVNUM","iRISETIMECNT","iBITTIMECNT","UNKNOWN"}; const char *v2s(UInt8 v) { if (v < 0x0b) return vstrs[v]; return vstrs[0x0b]; } */ void IOI2CControllerPPC::writeReg( int reg_index, UInt8 value) { // DLOG("wReg:%s, %02x\n",v2s(reg_index),value); *(iic[reg_index]) = value; eieio(); } UInt8 IOI2CControllerPPC::readReg( int reg_index) { UInt8 value = *(iic[reg_index]); eieio(); // DLOG("rReg:%s, %02x\n",v2s(reg_index),value); return value; } IOReturn IOI2CControllerPPC::setPowerState( unsigned long newPowerState, IOService *dontCare) { if (fU3NoSleep) { if (newPowerState == kIOI2CPowerState_SLEEP) { DLOG("IOI2CControllerPPC::setPowerState -> sleep (leave it on for PE4CPU)\n"); return IOPMAckImplied; } } return super::setPowerState(newPowerState, dontCare); }