/* * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * * AppleKiwiRoot.cpp * Need something to match the device registry entry. * * */ #include #include "AppleKiwiRoot.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DLOG #undef DLOG #endif // debugging features // define to enable debug of the root class //#define KIWI_ROOT_DEBUG 1 // define to enable debug of the bay handles //#define KIWI_BAY_DEBUG 1 // define to enable debug of the interrupt controller. //#define KIWI_IC_DEBUG 1 #ifdef KIWI_ROOT_DEBUG #define DLOG(fmt, args...) IOLog(fmt, ## args) #else #define DLOG(fmt, args...) #endif #define kDeviceTypeString "pci-ide" #define kDevicetypeKey "device_type" #define kNameKey "name" #define kNameString "AppleKiwi" #define kPCIInlineKey "pci_inline" #define kMode6Key "mode6" #define k100mhz_pll 0x0d2b #define k133mhz_pll 0x0826 //property keys as defined in the 4.4.2a3 boot rom. #define kTrayPresentStatusKey "platform-drivePresent" #define kBayInUseStatusKey "platform-driveInUse" #define kBayPowerStatusKey "platform-drivePower" #define kBayOpenSwitchStatusKey "platform-driveSwitch" #define kBaySetActiveLEDKey "platform-setDriveInUse" #define kBaySetFailLEDKey "platform-setDriveFail" #define kBaySetPowerKey "platform-setDrivePower" // enablers for event mechanism #define kEnableInsertEvent "enable-drivePresent" #define kEnableSwitchEvent "enable-driveSwitch" #define kDisableInsertEvent "disable-drivePresent" #define kDisableSwitchEvent "disable-driveSwitch" // constants used for registering handlers for bay events. #define kRegisterForInsertEvent "register-drivePresent" #define kRegisterForSwitchEvent "register-driveSwitch" // Bay event constants. #define kInsertEvent 'Inst' #define kSwitchEvent 'Swch' //--------------------------------------------------------------------------- #define super IOService OSDefineMetaClassAndStructors ( AppleKiwiRoot, IOService ) //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- bool AppleKiwiRoot::init(OSDictionary* properties) { DLOG("AppleKiwiRoot init start\n"); // Initialize instance variables. baseZeroMap = baseOneMap = baseTwoMap = baseThreeMap = baseFourMap = baseFiveMap = 0; baseAddrZero = baseAddrOne = baseAddrTwo = baseAddrThree = baseAddrFour = baseAddrFive = 0; pmRootDomain = 0; systemIsSleeping = false; chiplockOnBus = true; pdc271 = false; masterpllF = k100mhz_pll; if (super::init(properties) == false) { DLOG("AppleKiwiRoot: super::init() failed\n"); return false; } DLOG("AppleKiwiRoot init done\n"); return true; } /*--------------------------------------------------------------------------- * * Check and see if we really match this device. * override to accept or reject close matches based on further information ---------------------------------------------------------------------------*/ IOService* AppleKiwiRoot::probe(IOService* provider, SInt32* score) { OSData *compatibleEntry; DLOG("AppleKiwiRoot starting probe\n"); compatibleEntry = OSDynamicCast( OSData, provider->getProperty( "name" ) ); if ( compatibleEntry == 0 ) { // error unknown controller type. DLOG("AppleKiwiRoot failed getting compatible property\n"); return 0; } // test the compatible property for a match to the controller type name. if ( compatibleEntry->isEqualTo( kNameString, sizeof(kNameString)-1 ) == false ) { // not our type of controller DLOG("AppleKiwiRoot compatible property doesn't match\n"); return 0; } return this; } bool AppleKiwiRoot::start( IOService * provider ) { DLOG("AppleKiwiRoot: starting\n"); static const IOPMPowerState powerStates[ 2 ] = { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 1, IOPMPowerOn, IOPMPowerOn, IOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0 } }; IOPCIDevice *pciNub = (IOPCIDevice *)provider; if( !super::start( provider)) return( false); // test if pci inline is available if(pciNub->configRead8( kIOPCIConfigRevisionID) < 0x03) { chiplockOnBus = true; kiwiChipLock = IORecursiveLockAlloc(); if( !kiwiChipLock ) return false; DLOG("AppleKiwiRoot: pci inline not detected\n"); } else { DLOG("AppleKiwiRoot: pci inline available\n"); chiplockOnBus = false; conf40Val = pciNub->configRead8( 0x40); conf40Val |= 0x01; pciNub->configWrite8( 0x40, conf40Val); } // test if 20271 controller if(pciNub->configRead16( kIOPCIConfigDeviceID) == 0x6269) { pdc271 = true; masterpllF = k133mhz_pll; DLOG("AppleKiwiRoot: pdc271 detected\n"); } // Make sure IO space is on. pciNub->setIOEnable(true); // turn on the bus-mastering enable pciNub->setBusMasterEnable( true ); // enable mem access pciNub->setMemoryEnable(true); setupPDC270(provider); AppleKiwiIC* kiwiIC = new AppleKiwiIC; if( !kiwiIC ) { DLOG("AppleKiwiRoot: failed to create KiwiIC\n"); return false; } if( !kiwiIC->init(NULL) ) { DLOG("AppleKiwiRoot: failed to init KiwiIC\n"); return false; } //kiwiIC->attach( provider ); if( !kiwiIC->start(provider,baseAddrFive) ) { DLOG("AppleKiwiRoot: failed to start KiwiIC\n"); return false; } pmRootDomain = getPMRootDomain(); if (pmRootDomain != 0) { pmRootDomain->registerInterestedDriver(this); } // Show we are awake now: systemIsSleeping = false; PMinit(); registerPowerDriver(this,(IOPMPowerState *)powerStates,2); joinPMtree(this); // create the stack publishBelow(provider); DLOG("AppleKiwiRoot: started\n"); return( true); } /*--------------------------------------------------------------------------- * free() - the pseudo destructor. Let go of what we don't need anymore. * * ---------------------------------------------------------------------------*/ void AppleKiwiRoot::free() { if( baseZeroMap ) { baseZeroMap->release(); } if( baseOneMap ) { baseOneMap->release(); } if( baseTwoMap ) { baseTwoMap->release(); } if( baseThreeMap) { baseThreeMap->release(); } if( baseFourMap ) { baseFourMap->release(); } if( baseFiveMap ) { baseFiveMap->release(); } if( kiwiChipLock ) { IORecursiveLockFree( kiwiChipLock); kiwiChipLock = 0; } super::free(); } AppleKiwiDevice * AppleKiwiRoot::createNub( IORegistryEntry * from ) { AppleKiwiDevice * nub; nub = new AppleKiwiDevice; if( nub && !nub->init( from, gIODTPlane )) { nub->free(); nub = 0; } return( nub); } void AppleKiwiRoot::processNub(AppleKiwiDevice * nub) { if(!chiplockOnBus) { nub->setProperty(kPCIInlineKey, true ); } if( pdc271 ) { nub->setProperty(kMode6Key, true ); } nub->setDeviceMemory( getProvider()->getDeviceMemory()); nub->initProperties(); } void AppleKiwiRoot::publishBelow( IORegistryEntry * root ) { DLOG("AppleKiwiRoot publish below\n"); OSCollectionIterator * kids = 0; IORegistryEntry * next; AppleKiwiDevice * nub; // publish everything below, minus excludeList kids = IODTFindMatchingEntries( root, kIODTRecursive | kIODTExclusive, "('ata-disk','atapi-disk','disk')"); if( kids) { DLOG("AppleKiwiRoot found kids\n"); while( (next = (IORegistryEntry *)kids->getNextObject())) { if( 0 == (nub = createNub( next ))) continue; nub->attach( this ); processNub(nub); if( !nub->deviceIsPresent() ) { // float the bus pins if no drive is present; // figure out which bus this child is int busNum = 0; UInt32* gcrReg = (UInt32*) ( baseAddrFive + 0x1108 ); OSString* locationCompare = OSString::withCString( "1" ); if( locationCompare->isEqualTo( nub->getLocation() )) { gcrReg = (UInt32*) ( baseAddrFive + 0x1208 ); busNum = 1; } locationCompare->release(); *gcrReg |= 0x00000800; //LE format OSSynchronizeIO(); DLOG("AppleKiwiRoot - turning off empty bus %d\n", busNum); } } kids->release(); } } bool AppleKiwiRoot::compareNubName( const IOService * nub, OSString * name, OSString ** matched ) const { return( IODTCompareNubName( nub, name, matched ) || nub->IORegistryEntry::compareName( name, matched ) ); } IOReturn AppleKiwiRoot::getNubResources( IOService * nub ) { if( nub->getDeviceMemory()) return( kIOReturnSuccess ); IODTResolveAddressing( nub, "reg", getProvider()->getDeviceMemoryWithIndex(0) ); return( kIOReturnSuccess); } void AppleKiwiRoot::setupPDC270(IOService* provider) { IOPCIDevice *pciNub = (IOPCIDevice *)provider; baseFiveMap = pciNub->mapDeviceMemoryWithRegister( kIOPCIConfigBaseAddress5 ); if( baseFiveMap == 0 ) { DLOG("Failed to get base address maps\n"); return; } baseAddrFive = (UInt8*) baseFiveMap->getVirtualAddress(); DLOG("baseAddrFive = %lx \n", (UInt32) baseAddrFive); //Set pll program value OSWriteLittleInt16((void*) baseAddrFive, 0x1202, masterpllF); IOSleep( 10 ); // give it 10ms to stabilize. return; } void AppleKiwiRoot::getLock(bool lock) { if(!chiplockOnBus) return; if( lock ) { IORecursiveLockLock( kiwiChipLock); } else { IORecursiveLockUnlock( kiwiChipLock); } } IOReturn AppleKiwiRoot::powerStateWillChangeTo (IOPMPowerFlags theFlags, unsigned long, IOService* whichDevice) { if ( (whichDevice == pmRootDomain) && systemIsSleeping ) { if ((theFlags & IOPMPowerOn) || (theFlags & IOPMSoftSleep) ) { DLOG("KiwiRoot::powerStateWillChangeTo waking up\n"); systemIsSleeping = false; } //DLOG("KiwiRoot::powerStateDidChangeTo acknoledging power change flags = %lx\n", theFlags); } return IOPMAckImplied; } IOReturn AppleKiwiRoot::powerStateDidChangeTo (IOPMPowerFlags theFlags, unsigned long, IOService* whichDevice) { if ( ( whichDevice == pmRootDomain) && !systemIsSleeping ) { if (!(theFlags & IOPMPowerOn) && !(theFlags & IOPMSoftSleep) ) { DLOG("KiwiRoot::powerStateDidChangeTo going to sleep\n"); // time to go to sleep getLock(true); systemIsSleeping = true; } //DLOG("KiwiRoot::powerStateDidChangeTo acknoledging power change flags = %lx\n", theFlags); } return IOPMAckImplied; } IOReturn AppleKiwiRoot::setPowerState ( unsigned long powerStateOrdinal, IOService* whatDevice ) { if( powerStateOrdinal == 0 && systemIsSleeping ) { DLOG("AppleKiwiRoot power ordinal 0\n"); } if( powerStateOrdinal == 1 && !(systemIsSleeping) ) { DLOG("AppleKiwiRoot power ordinal 1\n"); // time to wake up IOPCIDevice *pciNub = (IOPCIDevice *)getProvider(); if(!chiplockOnBus) { pciNub->configWrite8( 0x40, conf40Val); } OSWriteLittleInt16((void*) baseAddrFive, 0x1202, masterpllF); IODelay( 250 ); // allow PLL to stabilise getLock(false); } return IOPMAckImplied; } #pragma mark - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifdef DLOG #undef DLOG #endif //#define KIWI_BAY_DEBUG 1 #ifdef KIWI_BAY_DEBUG #define DLOG(fmt, args...) IOLog(fmt, ## args) #else #define DLOG(fmt, args...) #endif #undef super #define super IOService OSDefineMetaClassAndStructors(AppleKiwiDevice, IOService); enum { kKiwiDevCPowerStateCount = 2 }; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ bool AppleKiwiDevice::init( IORegistryEntry * from, const IORegistryPlane * inPlane ) { bool result = super::init(from, inPlane); bayPHandle = 0; bayState = kBayInitialState; eventGate = kEventsOff; childBusState = kChildBusNone; currPwrState = 1; // initial state is power on and running. return result; } // setup the cookies from the properties published for this nub. void AppleKiwiDevice::initProperties(void) { OSData* registryData = 0; IOReturn retval; static const IOPMPowerState powerStates[ kKiwiDevCPowerStateCount ] = { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 1, IOPMPowerOn, IOPMPowerOn, IOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0 } }; registryData = OSDynamicCast( OSData, getProperty("AAPL,phandle") ); if( registryData ) { bayPHandle = * ((UInt32*) registryData->getBytesNoCopy(0,4) ); DLOG("Kiwi Device found %s with data %lx\n", kTrayPresentStatusKey, bayPHandle); } // register for Bay Events here // we care about insertion events and removal handle switch events. char callName[255]; if( bayPHandle ) { sprintf(callName,"%s-%8lx", kRegisterForInsertEvent, bayPHandle); retval = callPlatformFunction(callName, true, (void*) &sBayEventOccured, (void*)this, (void*)kInsertEvent, 0 ); DLOG("AppleKiwiDevice registering handler %s returned %x\n", callName, retval); } else { DLOG( "AppleKiwiDevice bayPHandle not found, handler not registerd\n" ); } if( bayPHandle ) { sprintf(callName,"%s-%8lx", kRegisterForSwitchEvent, bayPHandle); retval = callPlatformFunction(callName, true, (void*) &sBayEventOccured, (void*) this, (void*)kSwitchEvent, 0 ); DLOG("AppleKiwiDevice registering handler %s returned %x\n", callName, retval); } else { DLOG( "AppleKiwiDevice bayPHandle not found, handler not registerd\n" ); } pmRootDomain = getPMRootDomain(); if (pmRootDomain != 0) { pmRootDomain->registerInterestedDriver(this); } // Show we are awake now: systemIsSleeping = false; PMinit(); // register as controlling driver registerPowerDriver( this, (IOPMPowerState *) powerStates, kKiwiDevCPowerStateCount); // join the tree getProvider()->joinPMtree( this); // OSCompareAndSwap(kEventsOff, kEventsOn, &eventGate ); enableBayEvents(); } IOService* AppleKiwiDevice::matchLocation( IOService * /* client */ ) { // DLOG("KiwiDevice matchLocation\n"); return( this ); } IOReturn AppleKiwiDevice::getResources( void ) { return( ((AppleKiwiRoot *)getProvider())->getNubResources( this )); } IOReturn AppleKiwiDevice::message (UInt32 type, IOService* provider, void* argument) { // look for a sneaky message from the child if( provider == 0 ) { switch( type ) { case 'onli': // device has succesfully come up. childBusState = kChildBusOnline; setLEDColor( kLEDGreen ); break; case 'fail': // device is now down childBusState = kChildBusNone; setLEDColor( kLEDRed ); break; case 'ofln': setBayPower( kBayPowerOff); IOSleep(3000); setLEDColor( kLEDOff ); break; default: break; } } else { return super::message(type, provider, argument); } return kATANoErr; } /// Hot bay features // test to see if there's a device in the bay. bool AppleKiwiDevice::deviceIsPresent(void) { UInt32 isPresent = 0; IOReturn retval = 0; char callName[255]; if( bayPHandle ) { sprintf(callName,"%s-%8lx", kTrayPresentStatusKey, bayPHandle ); retval = callPlatformFunction(callName, false, (void *)&isPresent, 0, 0, 0); DLOG("AppleKiwiDevice call platform %s returned %d, isPresent = %lu\n", callName, retval, isPresent); } else { DLOG( "AppleKiwiDevice bayPHandle not found isPresent\n" ); } if(isPresent) { bayState = kBayDriveNotLocked; sprintf(callName,"%s-%8lx", kBayOpenSwitchStatusKey, bayPHandle ); retval = callPlatformFunction(callName, false, (void *)&isPresent, 0, 0, 0); DLOG("AppleKiwiDevice call platform %s returned %d, isPresent = %lu\n", callName, retval, isPresent); if(isPresent) { bayState = kBayDriveLocked; makeBayReady(0); registerService(); } else { handleBayRemoved(); } } else { handleBayRemoved(); bayState = kBayEmpty; } // now enable processing from the handle/bay switches. OSCompareAndSwap(kEventsOff, kEventsOn, &eventGate ); return isPresent; } // prepare bay by turning on power and enabling the interface void AppleKiwiDevice::makeBayReady(UInt32 delayMS) { setBayPower( kBayPowerOn); switch( childBusState ) { case kChildBusNone: case kChildBusStarting: // leave the LED alone. break; case kChildBusOnline: setLEDColor( kLEDGreen ); break; case kChildBusFail: setLEDColor(kLEDRed); break; } } // shutdown the bay when user starts removal void AppleKiwiDevice::handleBayRemoved(void) { // light is red now setLEDColor( kLEDRed ); // message the client (ata driver) with a kATARemovedEvent (IOATATypes.h) IORegistryEntry *myTempchild = getChildEntry(gIOServicePlane); // It mUst be an IOService to be terminated: IOService *ioServiceChild = OSDynamicCast(IOService, myTempchild); if (ioServiceChild != NULL) { DLOG("AppleKiwi Sending removal message\n"); messageClient(kATARemovedEvent, ioServiceChild, (void*)OSString::withCString( kNameString ), 0); // the child driver will message us when tear down is complete } else { DLOG("AppleKiwi removal event but no ioService child to message\n"); setBayPower( kBayPowerOff); setLEDColor( kLEDOff ); } childBusState = kChildBusNone; } void AppleKiwiDevice::enableBayEvents(void) { IOReturn retval; char callName[256]; sprintf(callName,"%s-%8lx", kEnableInsertEvent, bayPHandle); retval = callPlatformFunction(callName, true, (void*) &sBayEventOccured, (void*)this, (void*)kInsertEvent, 0 ); sprintf(callName,"%s-%8lx", kEnableSwitchEvent, bayPHandle); retval = callPlatformFunction(callName, true, (void*) &sBayEventOccured, (void*) this,(void*)kSwitchEvent, 0 ); DLOG("AppleKiwiDevice enable handler %s returned %x\n", callName, retval); } void AppleKiwiDevice::disableBayEvents(void) { IOReturn retval; char callName[256]; sprintf(callName,"%s-%8lx", kDisableInsertEvent, bayPHandle); retval = callPlatformFunction(callName, true, (void*) &sBayEventOccured, (void*)this, (void*)kInsertEvent, 0 ); sprintf(callName,"%s-%8lx", kDisableSwitchEvent, bayPHandle); retval = callPlatformFunction(callName, true, (void*) &sBayEventOccured, (void*) this,(void*)kSwitchEvent, 0 ); DLOG("AppleKiwiDevice disable handler %s returned %x\n", callName, retval); } // bring up a newly inserted device void AppleKiwiDevice::handleBayEvent(UInt32 event, UInt32 newData) { if( childBusState == kChildBusStarting ) { DLOG("Kiwi bus is still starting - handle bay event ignored.\n"); return; } // test for an unexpected removal event first, in case the drive is forced from the // socket or the handle is not working correctly as in EVT2 build. if( (event == kInsertEvent) && (newData == 0) // drive remove event && (bayState == kBayDriveLocked ) ) // wrong state!!! { DLOG("AppleKiwi drive was removed while bay in locked state!!!!\n"); handleBayRemoved(); bayState = kBayEmpty; return; } // normal operation is expected to be orderly with two kinds of events altering the state of the bay switch( bayState ) { case kBayInitialState: // initial state - unknown condition DLOG("AppleKiwi handleBay event status unknown!\n"); break; case kBayEmpty: // no drive present in bay // only legal event is device inserted if( (event == kInsertEvent) && (newData != 0) ) { bayState = kBayDriveNotLocked; DLOG( "AppleKiwi drive inserted\n"); // late change in handle design allows insertion with handle already closed. // this means that we won't get an event later. Check for this state and send ourself // a handle lock message if that happens. UInt32 bayBits = getBayStatus(); if( bayBits & 0x02) { // send lock event handleBayEvent( kSwitchEvent, 1); } } else { DLOG("AppleKiwi bay empty but got some other event!\n"); } break; case kBayDriveNotLocked: // drive present but handle unlocked. // two legal events - handle switch event or removal if( ( event == kInsertEvent ) && (newData == 0) ) { // bay is now empty bayState = kBayEmpty; DLOG("AppleKiwi bay empty\n"); } else if( (event == kSwitchEvent) && (newData != 0) ) { // bay is locked. childBusState = kChildBusStarting; setBayPower( kBayPowerOn); setLEDColor( kLEDOrange ); // when the ATADriver start succeeds, the light will turn green. DLOG("AppleKiwi bay latching. Data = %x\n", (unsigned int)newData); IOSleep( 1000 ); registerService(); bayState = kBayDriveLocked; DLOG("AppleKiwi handle locked, starting disk.\n"); } else { DLOG("AppleKiwi kBayDriveNotLocked got unexpected event\n"); } break; case kBayDriveLocked : // drive is present and locked in place. // only legal event is switch if( event == kSwitchEvent && newData == 0) { DLOG("AppleKiwi handle is being unlocked, stopping disk. Data = %x\n", (unsigned int) newData); handleBayRemoved(); bayState = kBayDrivePendRemoval; } else { DLOG("AppleKiwi kBayDriveLocked got unexpected event\n"); } break; case kBayDrivePendRemoval: // drive has been unlocked and must be removed for bay to activate again. if( (event == kInsertEvent) && (newData == 0) ) // drive remove event { // bay is now empty bayState = kBayEmpty; DLOG("AppleKiwi bay empty from pend-removal\n"); } else { DLOG("AppleKiwi event while in pend-removal state\n"); // ignore the event and leave the state in place. ; } break; // should never get here. default: DLOG("AppleKiwi handled unexpected default event\n"); break; } // enableBayEvents(); } // static handler registered with the system void AppleKiwiDevice::sBayEventOccured( void* p1, void* p2, void* p3, void* p4) { AppleKiwiDevice* me = (AppleKiwiDevice*) p1; if( OSCompareAndSwap( kEventsOn, kEventsOff, &(me->eventGate) ) ) { me->handleBayEvent( (UInt32)p2, (UInt32)p4 ); OSCompareAndSwap( kEventsOff, kEventsOn, &(me->eventGate) ); } } void AppleKiwiDevice::setLEDColor( UInt32 color) { char callGreen[255]; char callRed[255]; // enable power by calling platform function if( bayPHandle ) { sprintf(callGreen,"%s-%8lx", kBaySetActiveLEDKey, bayPHandle ); sprintf(callRed,"%s-%8lx", kBaySetFailLEDKey, bayPHandle ); switch( color ) { case kLEDOff: callPlatformFunction(callGreen, false, (void *)0, 0, 0, 0); callPlatformFunction(callRed, false, (void *)0, 0, 0, 0); break; case kLEDGreen: callPlatformFunction(callGreen, false, (void *)1, 0, 0, 0); callPlatformFunction(callRed, false, (void *)0, 0, 0, 0); break; case kLEDOrange: callPlatformFunction(callGreen, false, (void *)1, 0, 0, 0); callPlatformFunction(callRed, false, (void *)1, 0, 0, 0); break; case kLEDRed: callPlatformFunction(callGreen, false, (void *)0, 0, 0, 0); callPlatformFunction(callRed, false, (void *)1, 0, 0, 0); break; default: break; } } } void AppleKiwiDevice::setBayPower( UInt32 powerState ) { // disable power char callName[255]; // enable power by calling platform function if( bayPHandle ) { sprintf(callName,"%s-%8lx", kBaySetPowerKey, bayPHandle ); switch( powerState ) { case kBayPowerOn: callPlatformFunction(callName, false, (void*)1, 0, 0, 0); // pass 1 in first parm to enable power break; case kBayPowerOff: callPlatformFunction(callName, false, 0, 0, 0, 0); // pass 0 in first parm to disable power break; default: break; } } } UInt32 AppleKiwiDevice::getBayStatus( void ) { IOReturn errVal = 0; UInt32 platformStatus = 0; UInt32 bayBits = 0; // bit 0 = presence, bit 1 = handle state char callName[255]; sprintf(callName,"%s-%8lx", kTrayPresentStatusKey, bayPHandle ); errVal = callPlatformFunction(callName, false, (void *)&platformStatus, 0, 0, 0); if( platformStatus ) { bayBits |= 0x01; } platformStatus = 0; sprintf(callName,"%s-%8lx", kBayOpenSwitchStatusKey, bayPHandle ); errVal = callPlatformFunction(callName, false, (void *)&platformStatus, 0, 0, 0); if( platformStatus ) { bayBits |= (0x02); } if(bayBits == 0x02 ) { DLOG("Kiwidevice - bay handles in invalid state!!!\n"); ; } return bayBits; } IOReturn AppleKiwiDevice::setPowerState ( unsigned long powerStateOrdinal, IOService* whatDevice ) { if( powerStateOrdinal == 0 && systemIsSleeping ) { DLOG("KiwiDevice power ordinal 0\n"); currPwrState = 0; // offline or sleep. } UInt32 states[4] = { kBayEmpty, kBayDriveNotLocked, 0xffffffff, kBayDriveLocked}; // 00=empty, 01=present but not locked, 10=invalid state, 11=locked if( powerStateOrdinal == 1 && (currPwrState == 0) && !(systemIsSleeping) ) { DLOG("KiwiDevice power ordinal 1\n"); if( bayState == kBayDrivePendRemoval ) { // qualify a sleep event as the same as removal and insert back to the unlocked state bayState = kBayDriveNotLocked; } UInt32 bayBits = getBayStatus(); UInt32 bayStatNow = states[bayBits]; if( bayBits == 0x02 ) { // invalid state - handle locked but no carrier present? bayStatNow = kBayEmpty; } if( bayStatNow == bayState ) { //everything is cool just restore the previous power state if( bayState == kBayDriveLocked) { makeBayReady( 0 ); } else { setLEDColor( kLEDOff ); setBayPower( kBayPowerOff ); } } // something has changed across sleep - we will now callHandleBayEvent with // the event and the state to move the bay state machine along to reflect the current bay status. switch( bayState ) { case kBayEmpty: if( bayBits & 0x01 ) { // send insert event handleBayEvent( kInsertEvent, 1); } if( bayBits & 0x02) { // send lock event handleBayEvent( kSwitchEvent, 1); } break; case kBayDriveNotLocked: if( !(bayBits & 0x01) ) { // send remove event handleBayEvent( kInsertEvent, 0); } else if( bayBits & 0x02){ // send lock event handleBayEvent( kSwitchEvent, 1); } break; case kBayDriveLocked: if( !(bayBits & 0x02)) { //send unlock event handleBayEvent( kSwitchEvent, 0); } if( !(bayBits & 0x01) ) { // send remove event handleBayEvent( kInsertEvent, 0); } break; default: // nonsensical state break; } } return IOPMAckImplied; } IOReturn AppleKiwiDevice::powerStateWillChangeTo (IOPMPowerFlags theFlags, unsigned long, IOService* whichDevice) { if ( ( whichDevice == pmRootDomain) && !systemIsSleeping ) { if (!(theFlags & IOPMPowerOn) && !(theFlags & IOPMSoftSleep) ) { DLOG("AppleKiwiDevice::powerStateWillChangeTo going to sleep\n"); // time to go to sleep systemIsSleeping = true; } } return IOPMAckImplied; } IOReturn AppleKiwiDevice::powerStateDidChangeTo (IOPMPowerFlags theFlags, unsigned long, IOService* whichDevice) { if ( (whichDevice == pmRootDomain) && systemIsSleeping ) { if ((theFlags & IOPMPowerOn) || (theFlags & IOPMSoftSleep) ) { DLOG("AppleKiwiDevice::powerStateDidChangeTo waking up\n"); // time to wake up systemIsSleeping = false; } } return IOPMAckImplied; } #pragma mark - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifdef DLOG #undef DLOG #endif #ifdef KIWI_IC_DEBUG #define DLOG(fmt, args...) IOLog(fmt, ## args) #else #define DLOG(fmt, args...) #endif #undef super #define super IOService #undef super #define super IOInterruptController OSDefineMetaClassAndStructors(AppleKiwiIC, IOInterruptController); enum { kIOSLICPowerStateCount = 2 }; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ bool AppleKiwiIC::start(IOService *provider, UInt8* inBar5) { if( !provider || !inBar5 ) return false; // static const IOPMPowerState powerStates[ kIOSLICPowerStateCount ] = { // { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // { 1, IOPMPowerOn, IOPMPowerOn, IOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0 } // }; gcr0 = (volatile UInt32*) (inBar5 + 0x1108); gcr1 = (volatile UInt32*) (inBar5 + 0x1208); if (!super::start(provider)) { DLOG ("AppleKiwiIC::start - super::start(provider) returned false\n"); return false; } interruptControllerName = (OSSymbol *)IODTInterruptControllerName( provider ); if (interruptControllerName == 0) { DLOG ("AppleKiwiIC::start - interruptControllerName is NULL\n"); return false; } UInt32 numVectors = 2; UInt32 sizeofVectors = numVectors * sizeof(IOInterruptVector); // Allocate the memory for the vectors. vectors = (IOInterruptVector *)IOMalloc(sizeofVectors); if (vectors == NULL) { DLOG ("AppleKiwiIC::start - cannot allocate vectors\n"); return false; } bzero(vectors, sizeofVectors); // Allocate locks for the vectors. for (unsigned int cnt = 0; cnt < numVectors ; cnt++) { vectors[cnt].interruptLock = IOLockAlloc(); if (vectors[cnt].interruptLock == NULL) { for (cnt = 0; cnt < numVectors; cnt++) { if (vectors[cnt].interruptLock != NULL) IOLockFree(vectors[cnt].interruptLock); } DLOG ("AppleKiwiIC::start - cannot allocate lock for vectors[%d]\n", cnt); return false; } } // initialize the interrupt control bits to mask propogation *gcr0 &= (~ 0x00000200); // little endian format *gcr1 &= (~ 0x00000400); // little endian format provider->registerInterrupt(0, this, getInterruptHandlerAddress(), 0); // Register this interrupt controller so clients can find it. getPlatform()->registerInterruptController(interruptControllerName, this); provider->enableInterrupt(0); DLOG ("AppleKiwiIC::start - finished\n"); return true; } IOReturn AppleKiwiIC::getInterruptType(IOService *nub, int source, int *interruptType) { if (interruptType == 0) return kIOReturnBadArgument; *interruptType = kIOInterruptTypeLevel; return kIOReturnSuccess; } IOInterruptAction AppleKiwiIC::getInterruptHandlerAddress(void) { return (IOInterruptAction) &AppleKiwiIC::handleInterrupt ; } IOReturn AppleKiwiIC::handleInterrupt( void* /*refCon*/, IOService* /*nub*/, int /*source*/ ) { long vectorNumber = 0; IOInterruptVector *vector; // unsigned short events; // iterate each possible interrupt vector and let it decide whether it wants to // handle the interrupt. for(int i = 0; i < 2; i++) { vectorNumber = i; vector = &vectors[vectorNumber]; vector->interruptActive = 1; sync(); isync(); if (!vector->interruptDisabledSoft) { isync(); // Call the handler if it exists. if (vector->interruptRegistered) { vector->handler(vector->target, vector->refCon, vector->nub, vector->source); if (vector->interruptDisabledSoft) { // Hard disable the source. vector->interruptDisabledHard = 1; disableVectorHard(vectorNumber, vector); } } } else { // Hard disable the source. vector->interruptDisabledHard = 1; disableVectorHard(vectorNumber, vector); } vector->interruptActive = 0; } return kIOReturnSuccess; } bool AppleKiwiIC::vectorCanBeShared(long vectorNumber, IOInterruptVector *vector) { return false; } void AppleKiwiIC::initVector(long vectorNumber, IOInterruptVector *vector) { return; } void AppleKiwiIC::disableVectorHard(long vectorNumber, IOInterruptVector *vector) { switch( vectorNumber ) { case 0: //*gcr0 |= 0x00000200; // little endian formatted OSSynchronizeIO(); break; case 1: //*gcr1 |= 0x00000400; // little endian formatted OSSynchronizeIO(); break; default: break; // ??? should not happen } } void AppleKiwiIC::enableVector(long vectorNumber, IOInterruptVector *vector) { switch( vectorNumber ) { case 0: //*gcr0 &= (~ 0x00000200); // little endian formatted OSSynchronizeIO(); break; case 1: //*gcr1 &= (~ 0x00000400); // little endian formatted OSSynchronizeIO(); break; default: break; // ??? should not happen } } IOReturn AppleKiwiIC::setPowerState ( unsigned long powerStateOrdinal, IOService* whatDevice ) { return IOPMAckImplied; }