/** * PmPwrDnHandler() - Power down island/domain * @nodePtr Pointer to node-structure of power island/dom to be powered off * * @return Operation status of power down procedure (done by pmu-rom) */ static int PmPwrDnHandler(PmNode* const nodePtr) { int status = XST_PM_INTERNAL; if (NULL == nodePtr) { goto done; } /* Call proper PMU-ROM handler as needed */ switch (nodePtr->nodeId) { case NODE_FPD: status = PmPowerDownFpd(); break; case NODE_APU: status = XST_SUCCESS; break; case NODE_RPU: status = XpbrPwrDnRpuHandler(); break; default: PmDbg("unsupported node %s(%d)\n", PmStrNode(nodePtr->nodeId), nodePtr->nodeId); break; } if (XST_SUCCESS == status) { nodePtr->currState = PM_PWR_STATE_OFF; } done: PmDbg("%s\n", PmStrNode(nodePtr->nodeId)); return status; }
/** * PmRequirementReleaseAll() - Called when master primary processor is forced to * power down, so all requirements of the processor * are automatically released. * @master Master whose primary processor was forced to power down * * @return Status of the operation of releasing all slaves used by the master * and changing their state to the lowest possible. */ static int PmRequirementReleaseAll(const PmMaster* const master) { int status; PmRequirementId i; for (i = 0; i < master->reqsCnt; i++) { if (0U != (PM_MASTER_USING_SLAVE_MASK & master->reqs[i].info)) { /* Clear flag - master is not using slave anymore */ master->reqs[i].info &= ~PM_MASTER_USING_SLAVE_MASK; /* Release current and next requirements */ master->reqs[i].currReq = 0U; master->reqs[i].nextReq = 0U; /* Update slave setting */ status = PmUpdateSlave(master->reqs[i].slave); /* if pmu rom works correctly, status should be always ok */ if (XST_SUCCESS != status) { PmDbg("ERROR setting slave node %s\n", PmStrNode(master->reqs[i].slave->node.nodeId)); break; } } } return status; }
/** * PmWakeUpDisableAll() - Disable all wake-up sources of this master * @master Pointer to a master whose wake-up sources are to be disabled */ static void PmWakeUpDisableAll(PmMaster* const master) { u32 i; PmDbg("for %s\n", PmStrNode(master->procs->node.nodeId)); for (i = 0; i < master->reqsCnt; i++) { if (0U != (master->reqs[i].info & PM_MASTER_WAKEUP_REQ_MASK)) { PmMasterId r; bool hasOtherReq = false; master->reqs[i].info &= ~PM_MASTER_WAKEUP_REQ_MASK; /* * Check if there are other masters waiting for slave's * wake-up. */ for (r = 0U; r < master->reqs[i].slave->reqsCnt; r++) { if (0U != (master->reqs[i].slave->reqs[r]->info & PM_MASTER_WAKEUP_REQ_MASK)) { hasOtherReq = true; break; } } if (false == hasOtherReq) { /* * No other masters waiting for wake, disable * wake event. */ PmSlaveWakeDisable(master->reqs[i].slave); } } } }
/** * PmForcePowerDownChildren() - Forces power down for child nodes * @parent pointer to power object whose children are to be turned off */ static void PmForcePowerDownChildren(const PmPower* const parent) { u32 i; PmNode* child; PmProc* proc; for (i = 0U; i < parent->childCnt; i++) { child = parent->children[i]; if ((false != PmChildIsInLowestPowerState(child)) || (true != HAS_SLEEP(child->ops))) { continue; } PmDbg("Powering OFF child node %s\n", PmStrNode(child->nodeId)); /* Force the child's state to 0, which is its lowest power state */ child->ops->sleep(child); child->currState = 0U; /* Special case: node is a processor, release slave-requirements */ if (PM_TYPE_PROC == child->typeId) { proc = (PmProc*)child->derived; if ((NULL !=proc) && (true == proc->isPrimary)) { /* Notify master so it can release all requirements */ PmMasterNotify(proc->master, PM_PROC_EVENT_FORCE_PWRDN); } } } return; }
/** * PmOpportunisticSuspend() - After a node goes to sleep, try to power off * parents * @powerParent Pointer to the power node which should try to suspend, as well * its parents. */ void PmOpportunisticSuspend(PmPower* const powerParent) { PmPower* power = powerParent; if (NULL == powerParent) { goto done; } do { PmDbg("Opportunistic suspend attempt for %s\n", PmStrNode(power->node.nodeId)); if ((false == PmHasAwakeChild(power)) && (true == HAS_SLEEP(power->node.ops))) { /* Call sleep function of this power node */ power->node.ops->sleep(&power->node); power = power->node.parent; } else { power = NULL; } } while (NULL != power); done: return; }
/** * PmPwrUpHandler() - Power up island/domain * @nodePtr Pointer to node-structure of power island/dom to be powered on * * @return Operation status of power up procedure (done by pmu-rom) */ static int PmPwrUpHandler(PmNode* const nodePtr) { int status = XST_PM_INTERNAL; PmDbg("%s\n", PmStrNode(nodePtr->nodeId)); if (NULL == nodePtr) { goto done; } /* Call proper PMU-ROM handler as needed */ switch (nodePtr->nodeId) { case NODE_FPD: status = XpbrPwrUpFpdHandler(); if (XST_SUCCESS == status) { PmCrfRestoreContext(); } break; case NODE_APU: status = XST_SUCCESS; break; case NODE_RPU: { u32 reg; status = XpbrPwrUpRpuHandler(); /* release RPU island reset */ reg = Xil_In32(CRL_APB_RST_LPD_TOP); reg &= ~CRL_APB_RST_LPD_TOP_RPU_PGE_RESET_MASK; Xil_Out32(CRL_APB_RST_LPD_TOP, reg); break; } default: PmDbg("ERROR - unsupported node %s(%d)\n", PmStrNode(nodePtr->nodeId), nodePtr->nodeId); break; } if (XST_SUCCESS == status) { nodePtr->currState = PM_PWR_STATE_ON; } done: return status; }
/** * PmWakeUpCancelScheduled() - Cancel scheduled wake-up sources of the master * @master Pointer to a master whose scheduled wake-up sources should be * cancelled */ static void PmWakeUpCancelScheduled(PmMaster* const master) { u32 i; PmDbg("%s\n", PmStrNode(master->procs->node.nodeId)); for (i = 0; i < master->reqsCnt; i++) { master->reqs[i].info &= ~PM_MASTER_WAKEUP_REQ_MASK; } }
/** * PmInitSuspendCb() - request a master to suspend itself * @master Master to be asked to suspend * @reason The reason of initiating the suspend * @latency Not supported * @state State to which the master should suspend * @timeout How much time the master has to respond */ void PmInitSuspendCb(const PmMaster* const master, const u32 reason, const u32 latency, const u32 state, const u32 timeout) { PmDbg("of %s (%lu, %lu, %lu, %lu)\n", PmStrNode(master->nid), reason, latency, state, timeout); IPI_REQUEST5(master->pmuBuffer, PM_INIT_SUSPEND_CB, reason, latency, state, timeout); XPfw_Write32(IPI_PMU_0_TRIG, master->ipiMask); }
/** * PmRequirementUpdateScheduled() - Triggers the setting for scheduled * requirements * @master Master which changed the state and whose scheduled requirements are * triggered * @swap Flag stating should current/default requirements be saved as next * * a) swap=false * Set scheduled requirements of a master without swapping current/default and * next requirements - means the current requirements will be dropped and * default requirements has no effect. Upon every self suspend, master has to * explicitly re-request slave requirements. * b) swap=true * Set scheduled requirements of a master with swapping current/default and * next requirements (swapping means the current/default requirements will be * saved as next, and will be configured once master wakes-up). If the master * has default requirements, default requirements are saved as next instead of * current requirements. Default requirements has priority over current * requirements. */ static int PmRequirementUpdateScheduled(const PmMaster* const master, const bool swap) { int status; PmRequirementId i; PmDbg("%s\n", PmStrNode(master->procs[0].node.nodeId)); for (i = 0; i < master->reqsCnt; i++) { if (master->reqs[i].currReq != master->reqs[i].nextReq) { u32 tmpReq = master->reqs[i].nextReq; if (true == swap) { if (0U != master->reqs[i].defaultReq) { /* Master has default requirements for * this slave, default has priority over * current requirements. */ master->reqs[i].nextReq = master->reqs[i].defaultReq; } else { /* Save current requirements as next */ master->reqs[i].nextReq = master->reqs[i].currReq; } } master->reqs[i].currReq = tmpReq; /* Update slave setting */ status = PmUpdateSlave(master->reqs[i].slave); /* if rom works correctly, status should be always ok */ if (XST_SUCCESS != status) { PmDbg("ERROR setting slave node %s\n", PmStrNode(master->reqs[i].slave->node.nodeId)); break; } } } return status; }
/** * PmRequirementCancelScheduled() - Called when master aborts suspend, to cancel * scheduled requirements (slave capabilities requests) * @master Master whose scheduled requests should be cancelled */ void PmRequirementCancelScheduled(const PmMaster* const master) { PmRequirementId i; for (i = 0; i < master->reqsCnt; i++) { if (master->reqs[i].currReq != master->reqs[i].nextReq) { /* Drop the scheduled request by making it constant */ PmDbg("%s\n", PmStrNode(master->reqs[i].slave->node.nodeId)); master->reqs[i].nextReq = master->reqs[i].currReq; } } }
/** * PmHasAwakeChild() - Check whether power node has awake children * @power Pointer to PmPower object to be checked * * Function checks whether any child of the power provided as argument stops * power from being turned off. In the case of processor or power child, that * can be checked by inspecting currState value. For slaves, that is not the * case, as slave can be in non-off state just because the off state is entered * when power is turned off. This is the case when power parent is common for * multiple nodes. Therefore, slave does not block power from turning off if * it is unused and not in lowest power state. * * @return True if it has a child that is not off */ static bool PmHasAwakeChild(const PmPower* const power) { u32 i; bool hasAwakeChild = false; for (i = 0U; i < power->childCnt; i++) { if (false == PmChildIsInLowestPowerState(power->children[i])) { hasAwakeChild = true; PmDbg("%s\n", PmStrNode(power->children[i]->nodeId)); break; } } return hasAwakeChild; }
/** * PmEnableProxyWake() - Enable scheduled wake-up sources in GIC Proxy * @master Pointer to master whose scheduled wake-up sources should be enabled * * When FPD is powered down, wake-up sources should be enabled in GIC Proxy, * if APU's primary processor is gently put into a sleep. If APU is forced to * power down, this function will return without enabling GIC Proxy, because * after forced power down the processor can only be woken-up by an explicit * wake-up request through PM API. The check whether the processor is in sleep * state is performed in this function and not in pm_power from where this * function is called in order to keep pm_power independent from (not-aware of) * processor states. */ void PmEnableProxyWake(PmMaster* const master) { u32 i; if (master->procs->node.currState != PM_PROC_STATE_SLEEP) { goto done; } PmDbg("%s\n", PmStrNode(master->procs->node.nodeId)); for (i = 0; i < master->reqsCnt; i++) { if (master->reqs[i].info & PM_MASTER_WAKEUP_REQ_MASK) { PmSlaveWakeEnable(master->reqs[i].slave); } } done: return; }