/* * Copyright (c) 2004 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@ */ #include "AppleRS232Serial.h" // Globals #if USE_ELG extern com_apple_iokit_XTrace *gXTrace; extern UInt32 gTraceID; #endif /****** From iokit/IOKit/pwr_mgt/IOPMpowerState.h for reference (see ourPowerStates below) ****** struct IOPMPowerState { unsigned long version; // version number of this struct IOPMPowerFlags capabilityFlags; // bits that describe the capability IOPMPowerFlags outputPowerCharacter; // description (to power domain children) IOPMPowerFlags inputPowerRequirement; // description (to power domain parent) unsigned long staticPower; // average consumption in milliwatts unsigned long unbudgetedPower; // additional consumption from separate power supply (mw) unsigned long powerToAttain; // additional power to attain this state from next lower state (in mw) unsigned long timeToAttain; // (microseconds) unsigned long settleUpTime; // (microseconds) unsigned long timeToLower; // (microseconds) unsigned long settleDownTime; // (microseconds) unsigned long powerDomainBudget; // power in mw a domain in this state can deliver to its children }; *************************************************************************************************/ enum { kSCC_Settle_time = 500 // guess 500 microseconds }; enum { // just two power states supported, on and off. kSCC_PowerStateOff = 0, kSCC_PowerStateOn, kSCC_PowerStateCount }; static IOPMPowerState gOurPowerStates[kSCC_PowerStateCount] = { {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {1, IOPMDeviceUsable | IOPMMaxPerformance, IOPMPowerOn, IOPMPowerOn, 50, 0, 0, kSCC_Settle_time, kSCC_Settle_time, kSCC_Settle_time, kSCC_Settle_time, 0} }; #define super IOSerialDriverSync #pragma mark -- Class overrides /****************************************************************************************************/ // // Method: AppleRS232Serial::registerWithPolicyMaker // // Inputs: policyMaker - the power policy maker // // Outputs: Return code - various // // Desc: Initialize the driver for power management and register // ourselves with policy-maker. Called by superclass - not by // Power Management // /****************************************************************************************************/ bool AppleRS232Serial::initForPM(IOService *provider) { IOReturn rc; ELG(0, provider, "initForPM - entry. provider="); PMinit(); // Initialize superclass variables provider->joinPMtree(this); // Attach into the power management hierarchy if (pm_vars == NULL) { // Did it work ALERT(0, provider, "initForPM - joinPMtree failed"); return false; } // Initialize power management support state. // if async on/off might need two of these fPowerThreadCall = thread_call_allocate(handleSetPowerState, (thread_call_param_t) this ); if (fPowerThreadCall == NULL) { // check early, check often ALERT(0, 0, "initForPM - failed to allocate thread"); return false; } // We've done the PMinit, so we're the policy mgr. Register w/ourselves as the driver (yuck) rc = registerPowerDriver(this, gOurPowerStates, kSCC_PowerStateCount); if (rc) { ALERT(0, rc, "initForPM - failed to registerPowerDriver"); return false; } ELG(0, rc, "initForPM - exit. rc="); return true; }/* end registerWithPolicyMaker */ /****************************************************************************************************/ // // Method: AppleRS232Serial::setPowerState // // Inputs: powerStateOrdinal - the state being set // // Outputs: Return code - IOPMAckImplied or IOPMNoSuchState // // Desc: Power state is being set // /****************************************************************************************************/ IOReturn AppleRS232Serial::setPowerState(unsigned long powerStateOrdinal, IOService *whatDevice) { bool ok; UInt32 counter = 0; ELG(0, powerStateOrdinal, "setPowerState - powerStateOrdinal"); retain(); // paranoia is your friend, make sure we're not freed fWaitForGatedCmd = true; // could do this async, but let's be sync initially ok = thread_call_enter1(fPowerThreadCall, (void *)powerStateOrdinal); // schedule work on workloop if (ok) { // if thread was already pending ... ALERT(0, 0, "setPowerState - thread already pending?"); // a 'never' in current flow release(); // don't need/want the retain, so undo it } while (fWaitForGatedCmd) { // we're being sync for now, wait for it to finish IOSleep(2); // it should be very fast counter++; } ELG(0, counter, "setPowerState - finished after N sleeps of 2ms each"); return IOPMAckImplied; // we're done }/* end setPowerState */ #pragma mark -- Support and private Methods //--------------------------------------------------------------------------- // handleSetPowerState() // // param0 - the object // param1 - unsigned long, new power state ordinal //--------------------------------------------------------------------------- //static void AppleRS232Serial::handleSetPowerState(thread_call_param_t param0, thread_call_param_t param1 ) { AppleRS232Serial *self = OSDynamicCast(AppleRS232Serial, (const OSMetaClassBase *)param0); UInt32 on_off = (UInt32)param1; // new power state ELG(self, on_off, "handleSetPowerState - entry, self, on_off"); if (self && self->fCommandGate) { self->fCommandGate->runAction(&(self->setPowerStateGated), (void *)on_off, (void *)0, (void *)0, (void *)0); self->release(); // offset the retain in setPowerState() } ELG(param0, param1, "handleSetPowerState - exit"); } //--------------------------------------------------------------------------- // setPowerState() // // owner = the object // arg0 = boolean, true=power on, false=power off //--------------------------------------------------------------------------- // static IOReturn AppleRS232Serial::setPowerStateGated(OSObject *owner, void *arg0, void *arg1, void *arg2, void *arg3) { AppleRS232Serial *self = OSDynamicCast(AppleRS232Serial, (const OSMetaClassBase *)owner); UInt32 newState = (UInt32)arg0; // new power state to go to ELG(self, newState, "setPowerStateGated - entry. self, newState"); // sleep -> stop dma // wake -> resume dma // assumptions: not powering off scc cell, no need to fully reprogram the chip if (self && self->portOpened && self->fCurrentPowerState != newState) { SccChannel *channel = &self->fPort; if (newState) { ELG(self->fCurrentPowerState, newState, "setPowerStateGated - waking up, restarting dma"); // test - clear status change interrupt. CTS bounced on us, but that's ok. //SccWriteReg(channel, 0, kResetExtStsInt); // reset pending Ext/Sts interrupts //SccWriteReg(channel, 0, kResetExtStsInt); // and again in case it bounced on us (likely) // end test. SccEnableInterrupts(channel, kSccInterrupts); // Enable interrupts SccEnableInterrupts(channel, kRxInterrupts); // and on rx SccEnableInterrupts(channel, kTxInterrupts); // and on tx SccdbdmaStartReception(channel); // start up the read } else { ELG(self->fCurrentPowerState, newState, "setPowerStateGated - sleeping, stopping dma"); SccDisableInterrupts(channel, kSccInterrupts); // Disable scc interrupts before doing anything SccDisableInterrupts(channel, kRxInterrupts); // Disable the receiver SccDisableInterrupts(channel, kTxInterrupts); // Disable the transmitter SccdbdmaEndTransmission(channel); // stop any current tx SccdbdmaEndReception(channel); // stop the pending read } } self->fCurrentPowerState = newState; self->fWaitForGatedCmd = false; // release caller (power thread) ELG(0, 0, "setPowerStateGated - exit"); return kIOReturnSuccess; } /****************************************************************************************************/ // // Method: AppleRS232Serial::maxCapabilityForDomainState // // Inputs: domainState - current state // // Outputs: Return code - maximum state // // Desc: Returns the maximum state of card power, which would be // power on without any attempt to manager power // /****************************************************************************************************/ unsigned long AppleRS232Serial::maxCapabilityForDomainState(IOPMPowerFlags domainState) { ELG(0, domainState, "maxCapability called"); if (domainState & IOPMPowerOn ) { ELG(domainState, kSCC_PowerStateCount - 1, "maxCapabilityForDomainState, domainState=, returning 1"); return kSCC_PowerStateCount - 1; } ELG(domainState, 0, "maxCapabilityForDomainState, domainState=, returning 0"); return 0; }/* end maxCapabilityForDomainState */ /****************************************************************************************************/ // // Method: AppleRS232Serial::initialPowerStateForDomainState // // Inputs: domainState - current state // // Outputs: Return code - initial state // // Desc: The power domain may be changing state. If power is on in the new // state, that will not affect our state at all. If domain power is off, // we can attain only our lowest state, which is off // /****************************************************************************************************/ unsigned long AppleRS232Serial::initialPowerStateForDomainState(IOPMPowerFlags domainState) { ELG(0, domainState, "initialPowerState called, power flags"); if (domainState & IOPMPowerOn) { ELG(domainState, kSCC_PowerStateCount-1, "initialPowerStateForDomainState, domainState=, rtn 1"); return kSCC_PowerStateCount - 1; } ELG(domainState, 0, "initialPowerStateForDomainState, domainState=, rtn 0"); return 0; }/* end initialPowerStateForDomainState */ /****************************************************************************************************/ // // Method: AppleRS232Serial::powerStateForDomainState // // Inputs: domainState - current state // // Outputs: Return code - power state // // Desc: The power domain may be changing state. If power is on in the new // state, that will not affect our state at all. If domain power is off, // we can attain only our lowest state, which is off // /****************************************************************************************************/ unsigned long AppleRS232Serial::powerStateForDomainState(IOPMPowerFlags domainState ) { ELG(0, domainState, "power state from domain state called"); if (domainState & IOPMPowerOn) { ELG(domainState, 1, "powerStateForDomainState, domain=, rtn 1"); return 1; // This should answer What If? } ELG(domainState, 0, "powerStateForDomainState, domain=, rtn 0"); return 0; }/* end powerStateForDomainState */