/* * Copyright (C) 2014 - 2015 Xilinx, Inc. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * Use of the Software is limited solely to applications: * (a) running on a Xilinx device, or * (b) that interact with a Xilinx device through a bus or interconnect. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of the Xilinx shall not be used * in advertising or otherwise to promote the sale, use or other dealings in * this Software without prior written authorization from Xilinx. */ /************************************************************************* * PM slave structures definitions and code for handling states of slaves. ************************************************************************/ #include "pm_slave.h" #include "pm_master.h" #include "pm_defs.h" #include "pm_common.h" #include "pm_node.h" #include "pm_sram.h" #include "pm_usb.h" #include "pm_periph.h" #include "pm_pll.h" #include "pm_power.h" #include "lpd_slcr.h" /* Used for tracking number of enabled interrupts in each GIC Proxy group */ PmGicProxyProperties gicProxyGroups_g[FPD_GICP_GROUP_MAX] = { [FPD_GICP_GROUP0] = { .baseAddr = FPD_GICP_GROUP0_BASE_ADDR, .pmuIrqBit = FPD_GICP_PMU_IRQ_GROUP0, }, [FPD_GICP_GROUP1] = { .baseAddr = FPD_GICP_GROUP1_BASE_ADDR, .pmuIrqBit = FPD_GICP_PMU_IRQ_GROUP1, }, [FPD_GICP_GROUP2] = { .baseAddr = FPD_GICP_GROUP2_BASE_ADDR, .pmuIrqBit = FPD_GICP_PMU_IRQ_GROUP2, }, [FPD_GICP_GROUP3] = { .baseAddr = FPD_GICP_GROUP3_BASE_ADDR, .pmuIrqBit = FPD_GICP_PMU_IRQ_GROUP3, }, [FPD_GICP_GROUP4] = { .baseAddr = FPD_GICP_GROUP4_BASE_ADDR, .pmuIrqBit = FPD_GICP_PMU_IRQ_GROUP4, }, }; /* All slaves array */ static PmSlave* const pmSlaves[] = { &pmSlaveL2_g.slv, &pmSlaveOcm0_g.slv, &pmSlaveOcm1_g.slv, &pmSlaveOcm2_g.slv, &pmSlaveOcm3_g.slv, &pmSlaveTcm0A_g.slv, &pmSlaveTcm0B_g.slv, &pmSlaveTcm1A_g.slv, &pmSlaveTcm1B_g.slv, &pmSlaveUsb0_g.slv, &pmSlaveUsb1_g.slv, &pmSlaveTtc0_g.slv, &pmSlaveTtc1_g.slv, &pmSlaveTtc2_g.slv, &pmSlaveTtc3_g.slv, &pmSlaveSata_g.slv, &pmSlaveApll_g.slv, &pmSlaveVpll_g.slv, &pmSlaveDpll_g.slv, &pmSlaveRpll_g.slv, &pmSlaveIOpll_g.slv, &pmSlaveGpuPP0_g.slv, &pmSlaveGpuPP1_g.slv, &pmSlaveUart0_g, &pmSlaveUart1_g, &pmSlaveSpi0_g, &pmSlaveSpi1_g, &pmSlaveI2C0_g, &pmSlaveI2C1_g, &pmSlaveSD0_g, &pmSlaveSD1_g, &pmSlaveCan0_g, &pmSlaveCan1_g, &pmSlaveEth0_g, &pmSlaveEth1_g, &pmSlaveEth2_g, &pmSlaveEth3_g, &pmSlaveAdma_g, &pmSlaveGdma_g, &pmSlaveDP_g, &pmSlaveNand_g, &pmSlaveQSpi_g, &pmSlaveGpio_g, &pmSlaveAFI_g, }; /** * PmSlaveHasCapRequests() - Check whether slave has any request for any * capability * @slave Pointer to a slave whose requests are to be checked * * @return Based on checking all masters' requests function returns : * - true if there is at least one master requesting a capability * - false if no master is requesting anything from this slave */ bool PmSlaveHasCapRequests(const PmSlave* const slave) { u32 i; bool hasReq = false; for (i = 0U; i < slave->reqsCnt; i++) { if ((0U != (PM_MASTER_USING_SLAVE_MASK & slave->reqs[i]->info)) && (0U != slave->reqs[i]->currReq)) { /* Slave is used by this master and has current request for caps */ hasReq = true; break; } } return hasReq; } /** * PmGetMaxCapabilities()- Get maximum of all requested capabilities of slave * @slave Slave whose maximum required capabilities should be determined * * @return 32bit value encoding the capabilities */ static u32 PmGetMaxCapabilities(const PmSlave* const slave) { u32 i; u32 maxCaps = 0U; for (i = 0U; i < slave->reqsCnt; i++) { if (0U != (PM_MASTER_USING_SLAVE_MASK & slave->reqs[i]->info)) { maxCaps |= slave->reqs[i]->currReq; } } return maxCaps; } /** * PmCheckCapabilities() - Check whether the slave has state with specified * capabilities * @slave Slave pointer whose capabilities/states should be checked * @cap Check for these capabilities * * @return Status wheter slave has a state with given capabilities * - XST_SUCCESS if slave has state with given capabilities * - XST_NO_FEATURE if slave does not have such state */ int PmCheckCapabilities(PmSlave* const slave, const u32 cap) { PmStateId i; int status = XST_NO_FEATURE; for (i = 0; i < slave->slvFsm->statesCnt; i++) { /* Find the first state that contains all capabilities */ if ((cap & slave->slvFsm->states[i]) == cap) { status = XST_SUCCESS; break; } } return status; } /** * PmSlaveChangeState() - Change state of a slave * @slave Slave pointer whose state should be changed * @state New state * * @return XST_SUCCESS if transition was performed successfully. * Error otherwise. */ static int PmSlaveChangeState(PmSlave* const slave, const PmStateId state) { u32 t; int status; const PmSlaveFsm* fsm = slave->slvFsm; #ifdef DEBUG_PM PmStateId oldState = slave->node.currState; #endif if (0U == fsm->transCnt) { /* Slave's FSM has no transitions when it has only one state */ status = XST_SUCCESS; } else { /* * Slave has transitions to change the state. Assume the failure * and change status if state is changed correctly. */ status = XST_FAILURE; } for (t = 0U; t < fsm->transCnt; t++) { /* Find transition from current state to state to be set */ if ((fsm->trans[t].fromState != slave->node.currState) || (fsm->trans[t].toState != state)) { continue; } if ((0U != (slave->slvFsm->states[state] & PM_CAP_POWER)) && (NULL != slave->node.parent) && (true == IS_OFF(&slave->node.parent->node))) { /* Next state requires powering up power parent */ status = PmTriggerPowerUp(slave->node.parent); if (XST_SUCCESS != status) { goto done; } } if (NULL != slave->slvFsm->enterState) { /* Execute transition action of slave's FSM */ status = slave->slvFsm->enterState(slave, state); } else { /* * Slave's FSM has no actions, because it has no private * properties to be controlled here. */ status = XST_SUCCESS; } break; } #ifdef DEBUG_PM if (XST_SUCCESS == status) { PmDbg("%s %d->%d\n", PmStrNode(slave->node.nodeId), oldState, slave->node.currState); } else { PmDbg("%s ERROR #%d\n", PmStrNode(slave->node.nodeId), status); } #endif done: return status; } /** * PmGetStateWithCaps() - Get id of the state with provided capabilities * @slave Slave whose states are searched * @caps Capabilities the state must have * @state Pointer to a PmStateId variable where the result is put if * state is found * * @return Status of the operation * - XST_SUCCESS if state is found * - XST_NO_FEATURE if state with required capabilities does not * exist * * This function is to be called when state of a slave should be updated, * to find the slave's state with required capabilities. * Argument caps has included capabilities requested by all masters which * currently use the slave. Although these separate capabilities are validated * at the moment request is made, it could happen that there is no state that * has capabilities requested by all masters. This conflict has to be resolved * between the masters, so PM returns an error. */ static int PmGetStateWithCaps(const PmSlave* const slave, const u32 caps, PmStateId* const state) { PmStateId i; int status = XST_PM_CONFLICT; for (i = 0; i < slave->slvFsm->statesCnt; i++) { /* Find the first state that contains all capabilities */ if ((caps & slave->slvFsm->states[i]) == caps) { status = XST_SUCCESS; if (NULL != state) { *state = i; } break; } } return status; } /** * PmUpdateSlave() - Update the slave's state according to the current * requirements from all masters * @slave Slave whose state is about to be updated * * @return Status of operation of updating slave's state. */ int PmUpdateSlave(PmSlave* const slave) { PmStateId state; int status = XST_SUCCESS; u32 capsToSet = PmGetMaxCapabilities(slave); if (0U == capsToSet) { /* * Set the lowest power state as no capabilities are required. * This check has to exist because some slaves have no state * with 0 capabilities. Therefore, they are always placed in * first, lowest power state when their caps are not required. */ state = 0U; } else { /* Get state that has all required capabilities */ status = PmGetStateWithCaps(slave, capsToSet, &state); } if ((XST_SUCCESS == status) && (state != slave->node.currState)) { /* * Change state of a slave if state with required capabilities * exists and slave is not already in that state. */ status = PmSlaveChangeState(slave, state); } return status; } /** * PmSlaveWakeMasters() - Called when slave has to wake-up it's masters * @slave Pointer to a slave whose masters has to be woken-up (if master has * requested this slave as wake-up source before going to sleep) * * @return Return status of waking up processors * * @note: Wake event of this slave is disabled together with all other slaves * as part of the wake-up sequence. */ static int PmSlaveWakeMasters(PmSlave* const slave) { PmMasterId i; int status; int totalSt = XST_SUCCESS; for (i = 0U; i < slave->reqsCnt; i++) { if (slave->reqs[i]->info & PM_MASTER_WAKEUP_REQ_MASK) { PmDbg("%s->%s\n", PmStrNode(slave->node.nodeId), PmStrNode(slave->reqs[i]->requestor->procs->node.nodeId)); slave->reqs[i]->info &= ~PM_MASTER_WAKEUP_REQ_MASK; status = PmProcFsm(slave->reqs[i]->requestor->procs, PM_PROC_EVENT_WAKE); if (XST_SUCCESS != status) { /* * Failed waking up processor, remember * failure and try to wake-up others */ totalSt = status; } } } PmSlaveWakeDisable(slave); return totalSt; } /** * PmSlaveProcessWake() - Slave has generated wake-up interrupt, find both slave * source and master targets to and trigger wake-up. * @wakeMask Mask read from GPI1 register, based on which slave source that * generated interrupt will be determined. Master targets are * determined based on requirements for slave's wake-up capability. * * @return Status of performing wake-up. * * @note If multiple slaves has simultaneously generated interrupts (wake * events), they will be all processed in this function). For FPD * GIC Proxy this is a must because reading 32-bit status register * clears the interrupt, meaning that there could be up to 31 irqs * that would be lost if not handled immediately. */ int PmSlaveProcessWake(const u32 wakeMask) { int status = XST_SUCCESS; u32 g, s, irqStatus; if (!(PMU_LOCAL_GPI1_ENABLE_FPD_WAKE_GIC_PROX_MASK & wakeMask)) { goto done; } for (g = 0U; g < ARRAY_SIZE(gicProxyGroups_g); g++) { /* Reading status register clears interrupts */ irqStatus = XPfw_Read32(gicProxyGroups_g[g].baseAddr + FPD_GICP_STATUS_OFFSET); for (s = 0U; (0U != irqStatus) && (s < ARRAY_SIZE(pmSlaves)); s++) { if ((NULL != pmSlaves[s]->wake) && (pmSlaves[s]->wake->proxyIrqMask & irqStatus)) { status = PmSlaveWakeMasters(pmSlaves[s]); irqStatus &= ~pmSlaves[s]->wake->proxyIrqMask; } } } done: return status; } /** * PmWaitingForGicProxyWake() - Check is any Fpd wake is unmasked * @return True if there are some wake events unmasked, false otherwise */ static bool PmWaitingForGicProxyWake(void) { u32 i; bool waitingForWake = false; for (i = 0; i < ARRAY_SIZE(gicProxyGroups_g); i++) { u32 reg = XPfw_Read32(gicProxyGroups_g[i].baseAddr + FPD_GICP_MASK_OFFSET); if (FPD_GICP_ALL_IRQ_MASKED_IN_GROUP != reg) { waitingForWake = true; break; } } return waitingForWake; } /** * PmSlaveWakeEnable() - Enable wake interrupt of this slave * @slave Slave whose wake should be enabled */ void PmSlaveWakeEnable(PmSlave* const slave) { PmDbg("%s\n", PmStrNode(slave->node.nodeId)); if (NULL == slave->wake) { goto done; } /* Enable GIC Proxy IRQ */ XPfw_Write32(slave->wake->proxyGroup->baseAddr + FPD_GICP_IRQ_ENABLE_OFFSET, slave->wake->proxyIrqMask); /* Enable GIC Proxy group */ XPfw_Write32(LPD_SLCR_GICP_PMU_IRQ_ENABLE, slave->wake->proxyGroup->pmuIrqBit); /* Enable GPI1 FPD GIC Proxy wake event */ ENABLE_WAKE(PMU_LOCAL_GPI1_ENABLE_FPD_WAKE_GIC_PROX_MASK); done: return; } /** * PmSlaveWakeDisable() - Disable wake interrupt of this slave * @slave Slave whose wake should be disabled */ void PmSlaveWakeDisable(PmSlave* const slave) { PmDbg("%s\n", PmStrNode(slave->node.nodeId)); if (NULL == slave->wake) { goto done; } XPfw_Write32(slave->wake->proxyGroup->baseAddr + FPD_GICP_IRQ_DISABLE_OFFSET, slave->wake->proxyIrqMask); if (FPD_GICP_ALL_IRQ_MASKED_IN_GROUP == XPfw_Read32(slave->wake->proxyGroup->baseAddr + FPD_GICP_MASK_OFFSET)) { /* Disable group */ XPfw_Write32(LPD_SLCR_GICP_PMU_IRQ_DISABLE, slave->wake->proxyGroup->pmuIrqBit); if (false == PmWaitingForGicProxyWake()) { /* Disable FPD GPI1 wake event */ DISABLE_WAKE(PMU_LOCAL_GPI1_ENABLE_FPD_WAKE_GIC_PROX_MASK); } } done: return; }