embeddedsw/lib/sw_apps/zynqmp_pmufw/src/pm_slave.c
Filip Drazic 09090a4bc5 PMUFW: PM: slave: Added slave peripherals
Signed-off-by: Mirela Simonovic <mirela.simonovic@aggios.com>
Acked-by: Jyotheeswar Reddy Mutthareddyvari <jyothee@xilinx.com>
2015-07-31 16:55:40 +05:30

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;
}