
Signed-off-by: Mirela Simonovic <mirela.simonovic@aggios.com> Acked-by: Jyotheeswar Reddy Mutthareddyvari <jyothee@xilinx.com>
492 lines
14 KiB
C
492 lines
14 KiB
C
/*
|
|
* 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,
|
|
&pmSlaveGpuPP1_g,
|
|
&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;
|
|
}
|