/* * ======== Power_signalEvent ======== * Signal a Power event to registered clients. * */ Power_Status Power_signalEvent(Power_Event eventType, UArg eventArg1, UArg eventArg2, UInt notifyTimeout) { Power_Status status; UInt key; /* check for out of range event type */ if ((eventType < 0) || (eventType >= Power_INVALIDEVENT)) { return (Power_EINVALIDEVENT); } /* disable task scheduling */ key = Task_disable(); /* notify clients registered for this event */ status = Power_notify(eventType, notifyTimeout, Power_SigType_EXTERNAL, eventArg1, eventArg2); /* reenable task scheduling */ Task_restore(key); /* map a non-timeout error to Power_EFAIL */ if ((status != Power_ETIMEOUT) && (status != Power_SOK)) { status = Power_EFAIL; } return (status); }
/* * ======== Power_changeSetpoint ======== * Initiate a change to the F/V setpoint of the CPU, or the peripheral domain. * */ Power_Status Power_changeSetpoint(Power_Domain domain, UInt newSetpoint, UInt notifyTimeout) { PSCL_Status statusPSCL = PSCL_CANNOT_CHANGE_SETPOINT; Power_Status status = Power_SOK; Power_Status statusNotifyCPU = Power_SOK; Power_Status statusNotifyPER = Power_SOK; Bool notifyCPU = FALSE; Bool notifyPER = FALSE; UInt willChange = FALSE; Bool proceed = FALSE; Types_FreqHz cpuFreq; UInt previousSPPER; UInt previousSP; UInt disallowed; PSCL_ClkID clk; UInt frequency; UInt voltage; UInt maskSP; UInt taskKey; UInt swiKey; UInt key; /* make sure V/F scaling is supported */ if (!ti_sysbios_family_c674_Power_enableScaling) { status = Power_ENOTIMPLEMENTED; } /* check to make sure V/F scaling initialized OK */ else if (Power_module->PSCLinitOK == FALSE) { status = Power_EINITFAILURE; } /* check that domain ID is valid */ else if ((domain != Power_CPU) && (domain != Power_PER)) { status = Power_EINVALIDVALUE; } /* check if setpoint ID is out of range for the specified domain ID */ else if (((domain == Power_CPU) && (newSetpoint > (Power_module->numSPCPU - 1))) || ((domain == Power_PER) && (newSetpoint > (Power_module->numSPPER - 1)))) { status = Power_EOUTOFRANGE; } else { /* convert requested setpoint into a mask bit */ maskSP = 1 << newSetpoint; /* go atomic */ key = Hwi_disable(); /* now check the requested SP against disallowed SP constraints */ if (domain == Power_CPU) { disallowed = maskSP & Power_module->disallowedSetpointsCPU; } else { disallowed = maskSP & Power_module->disallowedSetpointsPER; } /* if the SP is *not* disallowed, check and set the 'busy' flag */ if (disallowed == 0) { if (Power_module->busy == FALSE) { Power_module->busy = TRUE; proceed = TRUE; } else { status = Power_EBUSY; } } else { status = Power_ENOTSUPPORTED; } /* end ATOMIC */ Hwi_restore(key); if (proceed == TRUE) { /* determine PSCL clock ID */ clk = (PSCL_ClkID) domain == Power_CPU ? PSCL_CPU_CLK : PSCL_PER_CLK; /* disable Task scheduling; allow Swi & Hwi for completion */ taskKey = Task_disable(); /* set flag for domain to be notified for SP change */ if (domain == Power_CPU) { notifyCPU = TRUE; } else { notifyPER = TRUE; } /* if voltage scaling enabled, call to PSCL to see if this setpoint change will result in a voltage change; if yes, then must notify for other clk domain too, as there will be a voltage scale even though that clk frequency isn't changing */ if (Power_module->currentConfig.scaleVoltage == TRUE) { PSCL_queryWillChangeVoltage(clk, newSetpoint, &willChange); if (willChange == TRUE) { if (domain == Power_CPU) { notifyPER = TRUE; } else { notifyCPU = TRUE; } } } /* now send any pre-notifications for CPU domain */ if (notifyCPU == TRUE) { /* set nextSP to indicate the pending setpoint */ if (domain == Power_CPU) { Power_module->nextSP = newSetpoint; /* yes, new setpoint */ previousSP = Power_module->currentSetpointCPU; } else { /* not new CPU setpoint */ Power_module->nextSP = Power_module->currentSetpointCPU; previousSP = Power_module->currentSetpointCPU; } /* notify clients registered for pre CPU SP notification */ statusNotifyCPU = Power_notify(Power_PENDING_CPU_SETPOINTCHANGE, notifyTimeout, Power_SigType_INTERNAL, NULL, NULL); } /* now send any pre-notifications for PER domain */ if ((statusNotifyCPU == Power_SOK) && (notifyPER == TRUE)) { /* set nextSPPER to indicate the pending setpoint */ if (domain == Power_CPU) { /* not new PER SP */ Power_module->nextSPPER = Power_module->currentSetpointPER; previousSPPER = Power_module->currentSetpointPER; } else { Power_module->nextSPPER = newSetpoint; /* yes, new PER SP */ previousSPPER = Power_module->currentSetpointPER; } /* notify clients registered for pre PER SP notification */ statusNotifyPER = Power_notify(Power_PENDING_PER_SETPOINTCHANGE, notifyTimeout, Power_SigType_INTERNAL, NULL, NULL); } /* if all 'pre' notifications succeeded... do the scaling op */ if ((statusNotifyCPU == Power_SOK) && (statusNotifyPER == Power_SOK)) { swiKey = Swi_disable(); /* call to PSCL to change the setpoint */ statusPSCL = PSCL_changeSetpoint(clk, newSetpoint, Power_module->currentConfig.scaleVoltage, Power_module->currentConfig.waitForVoltageScale,NULL,NULL); Swi_restore(swiKey); } /* if scaling operation successful... */ if (statusPSCL == PSCL_OK) { /* update previous and current SP globals */ if (notifyCPU == TRUE) { Power_module->currentSetpointCPU = Power_module->nextSP; Power_module->previousSP = previousSP; } if (notifyPER == TRUE) { Power_module->currentSetpointPER = Power_module->nextSPPER; Power_module->previousSPPER = previousSPPER; } /* if scaled the CPU frequency... tell BIOS about it */ if (domain == Power_CPU) { Power_getSetpointInfo(Power_CPU, newSetpoint, &frequency, &voltage); cpuFreq.lo = frequency * 1000; /* convert kHz to Hz */ cpuFreq.hi = 0; BIOS_setCpuFreq(&cpuFreq); } /* now notify post-notification clients */ if (notifyCPU == TRUE) { /* notify clients registered of post CPU SP change */ statusNotifyCPU = Power_notify(Power_DONE_CPU_SETPOINTCHANGE, notifyTimeout, Power_SigType_INTERNAL, NULL, NULL); } if ((statusNotifyCPU == Power_SOK) && (notifyPER == TRUE)) { /* notify clients registered of post PER SP change */ statusNotifyPER = Power_notify(Power_DONE_PER_SETPOINTCHANGE, notifyTimeout, Power_SigType_INTERNAL, NULL, NULL); } } /* done, so clear Power busy flag */ Power_module->busy = FALSE; /* resume Tasking */ Task_restore(taskKey); /* figure out return code */ if (statusPSCL == PSCL_INVALID_SETPOINT) { status = Power_EOUTOFRANGE; /* invalid SP according to PSCL */ } else if ((statusNotifyCPU == Power_ETIMEOUT) || (statusNotifyPER == Power_ETIMEOUT) ) { status = Power_ETIMEOUT; /* notification timeout */ } else if ((status != Power_SOK) || (statusPSCL != PSCL_OK)) { status = Power_EFAIL; /* convert to 'general failure' */ } } } return (status); }
/* * ======== Power_sleep ======== */ Power_Status Power_sleep(Power_SleepState sleepState, UArg arg0, UArg arg1) { Power_Status status = Power_SOK; UInt xosc_hf_active = FALSE; Power_Event postEventLate; UInt32 poweredDomains = 0; Bool exitNow = FALSE; Power_Event preEvent; Power_Event postEvent; UInt32 constraints; Bool retainCache = FALSE; UInt32 modeVIMS; UInt taskKey; UInt swiKey; UInt hwiKey; /* first validate the sleep code */ if ( sleepState != Power_STANDBY) { status = Power_EFAIL; } if (status == Power_SOK) { /* make sure Power is not still busy with a previous transition */ hwiKey = Hwi_disable(); if (Power_module->state == Power_ACTIVE) { /* set transition state to entering sleep */ Power_module->state = Power_ENTERING_SLEEP; } else { exitNow = TRUE; } Hwi_restore(hwiKey); if (exitNow == TRUE) { status = Power_EBUSY; } else { /* setup sleep vars */ if (sleepState == Power_STANDBY) { preEvent = Power_ENTERING_STANDBY; postEvent = Power_AWAKE_STANDBY; postEventLate = Power_AWAKE_STANDBY_LATE; } /* disable Task scheduling; allow Swis and Hwis for notifications */ taskKey = Task_disable(); /* signal all clients registered for pre-sleep notification */ status = Power_notify(preEvent); /* check for any error */ if (status != Power_SOK) { Power_module->state = Power_ACTIVE; Task_restore(taskKey); /* re-enable scheduler */ return (status); } /* now disable Swi scheduling */ swiKey = Swi_disable(); /* freeze the IOs on the boundary between MCU and AON */ AONIOCFreezeEnable(); /* if XOSC_HF is active, force it off */ if(OSCClockSourceGet(OSC_SRC_CLK_HF) == OSC_XOSC_HF) { xosc_hf_active = TRUE; ti_sysbios_family_arm_cc26xx_Power_XOSC_HF(DISABLE); } /* allow AUX to power down */ AONWUCAuxWakeupEvent(AONWUC_AUX_ALLOW_SLEEP); /* make sure writes take effect */ SysCtrlAonSync(); /* invoke specific sequences to activate sleep states... */ if (sleepState == Power_STANDBY) { /* query and save domain states before powering them off */ if (Power_getDependencyCount(DOMAIN_RFCORE)) { poweredDomains |= PRCM_DOMAIN_RFCORE; } if (Power_getDependencyCount(DOMAIN_SERIAL)){ poweredDomains |= PRCM_DOMAIN_SERIAL; } if (Power_getDependencyCount(DOMAIN_PERIPH)) { poweredDomains |= PRCM_DOMAIN_PERIPH; } /* gate running deep sleep clocks for Crypto and DMA */ if (Power_getDependencyCount(PERIPH_CRYPTO)) { PRCMPeripheralDeepSleepDisable( ti_sysbios_family_arm_cc26xx_Power_db[ PERIPH_CRYPTO].driverlibID); } if (Power_getDependencyCount(PERIPH_UDMA)) { PRCMPeripheralDeepSleepDisable( ti_sysbios_family_arm_cc26xx_Power_db[ PERIPH_UDMA].driverlibID); } /* make sure clock settings take effect */ PRCMLoadSet(); /* request power off of domains in the MCU voltage domain */ PRCMPowerDomainOff(poweredDomains | PRCM_DOMAIN_CPU); /* request uLDO during standby */ PRCMMcuUldoConfigure(true); /* query constraints to determine if cache should be retained */ constraints = Power_getConstraintInfo(); if ((constraints & Power_SB_VIMS_CACHE_RETAIN) != 0) { retainCache = TRUE; } /* if don't want retention in standby, disable it now ... */ if (retainCache == FALSE) { modeVIMS = VIMSModeGet(VIMS_BASE); /* wait if invalidate in progress... */ while (modeVIMS == VIMS_MODE_CHANGING) { modeVIMS = VIMSModeGet(VIMS_BASE); } PRCMCacheRetentionDisable(); VIMSModeSet(VIMS_BASE, VIMS_MODE_OFF); } /* setup recharge parameters */ SysCtrlSetRechargeBeforePowerDown(XoscInHighPowerMode); /* make sure all writes have taken effect */ SysCtrlAonSync(); /* invoke deep sleep to go to STANDBY */ PRCMDeepSleep(); /* if didn't retain cache in standby, re-enable retention now */ if (retainCache == FALSE) { VIMSModeSet(VIMS_BASE, modeVIMS); PRCMCacheRetentionEnable(); } /* force power on of AUX to keep it on when system is not * sleeping; this also counts as a write to the AON interface * ensuring that a following sync of the AON interface will * force an update of all registers */ AONWUCAuxWakeupEvent(AONWUC_AUX_WAKEUP); while(!(AONWUCPowerStatusGet() & AONWUC_AUX_POWER_ON)) {}; /* if XOSC_HF was forced off above, initiate switch back */ if (xosc_hf_active == TRUE) { ti_sysbios_family_arm_cc26xx_Power_XOSC_HF(ENABLE); } /* restore power domain states in effect before standby */ PRCMPowerDomainOn(poweredDomains); while (PRCMPowerDomainStatus(poweredDomains) != PRCM_DOMAIN_POWER_ON){}; /* restore deep sleep clocks of Crypto and DMA */ if (Power_getDependencyCount(PERIPH_CRYPTO)) { PRCMPeripheralDeepSleepEnable( ti_sysbios_family_arm_cc26xx_Power_db[ PERIPH_CRYPTO].driverlibID); } if (Power_getDependencyCount(PERIPH_UDMA)) { PRCMPeripheralDeepSleepEnable( ti_sysbios_family_arm_cc26xx_Power_db[ PERIPH_UDMA].driverlibID); } /* make sure clock settings take effect */ PRCMLoadSet(); } /* release request for uLDO */ PRCMMcuUldoConfigure(false); /* set transition state to EXITING_SLEEP */ Power_module->state = Power_EXITING_SLEEP; /* * signal clients registered for early post-sleep notification; * this should be used to initialize any timing critical or IO * dependent hardware */ status = Power_notify(postEvent); /* disable IO freeze and ensure RTC shadow value is updated */ AONIOCFreezeDisable(); SysCtrlAonSync(); /* re-enable interrupts */ CPUcpsie(); /* signal all clients registered for late post-sleep notification */ status = Power_notify(postEventLate); /* now clear the transition state before re-enabling scheduler */ Power_module->state = Power_ACTIVE; /* re-enable Swi scheduling */ Swi_restore(swiKey); /* adjust recharge parameters */ SysCtrlAdjustRechargeAfterPowerDown(); /* re-enable Task scheduling */ Task_restore(taskKey); /* check for any notification error */ if (status != Power_SOK) { return (status); } } } return (status); }
/* * ======== Power_sleepDSP ======== */ Power_Status Power_sleepDSP(UInt sleepCode, UInt sleepArg, UInt notifyTimeout) { Power_Status status = Power_SOK; Bool exitNow = FALSE; Power_Event preEvent; Power_Event postEvent; PMI_Sleep sleepMode; UInt taskKey; UInt swiKey; UInt hwiKey; /* first validate the sleep code */ if ( (sleepCode != Power_STANDBY) && (sleepCode != Power_SLEEP) && (sleepCode != Power_DEEPSLEEP) ) { status = Power_ENOTIMPLEMENTED; } /* make sure sleep request doesn't violate a registered constraint */ else if ( ( (sleepCode == Power_STANDBY) && ((Power_module->disallowedSleepModes & Power_STANDBY) != 0) ) || ( (sleepCode == Power_SLEEP) && ((Power_module->disallowedSleepModes & Power_SLEEP) != 0) ) || ( (sleepCode == Power_DEEPSLEEP) && ((Power_module->disallowedSleepModes & Power_DEEPSLEEP) != 0))) { status = Power_ENOTSUPPORTED; } /* check for valid sleepArg */ else if (sleepCode == Power_DEEPSLEEP) { if ((sleepArg != Power_EXTERNAL) && (sleepArg != Power_RTC_ALARM)) { status = Power_EINVALIDVALUE; } } if (status == Power_SOK) { /* make sure Power is not still busy with a previous transition */ hwiKey = Hwi_disable(); if (Power_module->busy == FALSE) { Power_module->busy = TRUE; } else { exitNow = TRUE; } Hwi_restore(hwiKey); if (exitNow == TRUE) { status = Power_EBUSY; } else { /* setup sleep vars */ if (sleepCode == Power_STANDBY) { preEvent = Power_GOINGTOSTANDBY; postEvent = Power_AWAKEFROMSTANDBY; sleepMode = PMI_STANDBY; } else if (sleepCode == Power_SLEEP) { preEvent = Power_GOINGTOSLEEP; postEvent = Power_AWAKEFROMSLEEP; sleepMode = PMI_SLEEP; } else { preEvent = Power_GOINGTODEEPSLEEP; postEvent = Power_AWAKEFROMDEEPSLEEP; sleepMode = PMI_DEEPSLEEP; } /* disable Task scheduling; allow Swis and Hwis for notifications */ taskKey = Task_disable(); /* signal all clients registered for pre-sleep notification */ status = Power_notify(preEvent, notifyTimeout, Power_SigType_INTERNAL, NULL, NULL); /* check for timeout or any other error */ if (status != Power_SOK) { Power_module->busy = FALSE; /* clear busy */ Task_restore(taskKey); /* re-enable scheduler */ return (status); } /* now disable Swi scheduling */ swiKey = Swi_disable(); /* start the sleep sequence */ hwiKey = Hwi_disable(); /* call to PMI to go to and wake from sleep... */ PMI_sleepCPU(sleepMode, Power_module->currentConfig.scaleVoltage, (UInt) sleepArg); /* when get here CPU has already processed the wakeup interrupt */ /* restore the previous interrupt enable state */ Hwi_restore(hwiKey); /* re-enable Swi scheduling */ Swi_restore(swiKey); /* signal all clients registered for post-sleep notification */ status = Power_notify(postEvent, notifyTimeout, Power_SigType_INTERNAL, NULL, NULL); /* now clear the busy flag before re-enabling scheduler */ Power_module->busy = FALSE; /* re-enable Task scheduling */ Task_restore(taskKey); /* check for timeout or other notification error */ if (status != Power_SOK) { return (status); } } } return (status); }
/* * ======== Power_shutdown ======== */ Power_Status Power_shutdown(UArg arg) { Power_Status status = Power_EFAIL; Bool exitNow = FALSE; UInt32 constraints; UInt hwiKey; /* make sure shutdown request doesn't violate a constraint */ constraints = Power_getConstraintInfo(); if ((constraints & (Power_SD_DISALLOW)) != 0) { status = Power_ECHANGE_NOT_ALLOWED; } if (status == Power_EFAIL) { /* make sure Power is not still busy with a previous transition */ hwiKey = Hwi_disable(); if (Power_module->state == Power_ACTIVE) { /* set new transition state to entering shutdown */ Power_module->state = Power_SHUTDOWN; } else { exitNow = TRUE; } Hwi_restore(hwiKey); if (exitNow == TRUE) { status = Power_EBUSY; } else { /* disable interrupts as start the shutdown sequence */ Hwi_disable(); /* signal all clients registered for pre-shutdown notification */ status = Power_notify(Power_ENTERING_SHUTDOWN); /* check for any error */ if (status != Power_SOK) { Power_module->state = Power_ACTIVE; CPUcpsie(); return (status); } /* proceed with shutdown sequence ... */ /* switch to RCOSC_HF and RCOSC_LF */ OSCInterfaceEnable(); if(OSCClockSourceGet(OSC_SRC_CLK_HF) != OSC_RCOSC_HF) { OSCClockSourceSet(OSC_SRC_CLK_HF | OSC_SRC_CLK_MF, OSC_RCOSC_HF); while(!OSCHfSourceReady()); OSCHfSourceSwitch(); } OSCClockSourceSet(OSC_SRC_CLK_LF,OSC_RCOSC_LF); while(OSCClockSourceGet(OSC_SRC_CLK_LF) != OSC_RCOSC_LF); OSCInterfaceDisable(); /* make sure DMA and CRYTO clocks are off in deep-sleep */ PRCMPeripheralDeepSleepDisable(PRCM_PERIPH_CRYPTO); PRCMPeripheralDeepSleepDisable(PRCM_PERIPH_UDMA); PRCMLoadSet(); while(!PRCMLoadGet()){}; /* power OFF AUX and disconnect from bus */ AUXWUCPowerCtrl(AUX_WUC_POWER_OFF); /* remove AUX force ON */ HWREG(AON_WUC_BASE + AON_WUC_O_AUXCTL) &= ~AON_WUC_AUXCTL_AUX_FORCE_ON; /* * reset AON event source IDs to avoid pending events powering * on MCU/AUX */ HWREG(AON_EVENT_BASE + AON_EVENT_O_MCUWUSEL) = 0x3F3F3F3F; HWREG(AON_EVENT_BASE + AON_EVENT_O_AUXWUSEL) = 0x003F3F3F; /* sync AON */ HWREG(AON_RTC_BASE + AON_RTC_O_SYNC); /* * enable shutdown - this latches the IOs, so configuration of * IOCFGx registers must be done prior to this */ AONWUCShutDownEnable(); /* sync AON */ HWREG(AON_RTC_BASE + AON_RTC_O_SYNC); /* wait until AUX powered off */ while (AONWUCPowerStatusGet() & AONWUC_AUX_POWER_ON); /* request to power off MCU when go to deep sleep */ PRCMMcuPowerOff(); /* turn off power domains inside MCU VD (BUS, FL_BUS, RFC, CPU) */ PRCMPowerDomainOff(PRCM_DOMAIN_RFCORE | PRCM_DOMAIN_SERIAL | PRCM_DOMAIN_PERIPH | PRCM_DOMAIN_CPU | PRCM_DOMAIN_VIMS); /* deep sleep to activate shutdown */ PRCMDeepSleep(); } } Power_module->state = Power_ACTIVE; /* if get here failed to shutdown, return failure code */ return (Power_EFAIL); }