diff --git a/lib/sw_apps/zynqmp_pmufw/src/pm_common.c b/lib/sw_apps/zynqmp_pmufw/src/pm_common.c index 93d5c1e2..3e65d031 100644 --- a/lib/sw_apps/zynqmp_pmufw/src/pm_common.c +++ b/lib/sw_apps/zynqmp_pmufw/src/pm_common.c @@ -99,6 +99,16 @@ const char* PmStrNode(const u32 node) return "NODE_TTC_0"; case NODE_SATA: return "NODE_SATA"; + case NODE_APLL: + return "NODE_APLL"; + case NODE_VPLL: + return "NODE_VPLL"; + case NODE_DPLL: + return "NODE_DPLL"; + case NODE_RPLL: + return "NODE_RPLL"; + case NODE_IOPLL: + return "NODE_IOPLL"; default: return "ERROR_NODE"; } diff --git a/lib/sw_apps/zynqmp_pmufw/src/pm_defs.h b/lib/sw_apps/zynqmp_pmufw/src/pm_defs.h index 0f9a8b2e..cdfe1e72 100644 --- a/lib/sw_apps/zynqmp_pmufw/src/pm_defs.h +++ b/lib/sw_apps/zynqmp_pmufw/src/pm_defs.h @@ -124,9 +124,14 @@ #define NODE_USB_1 23U #define NODE_TTC_0 24U #define NODE_SATA 25U +#define NODE_APLL 26U +#define NODE_VPLL 27U +#define NODE_DPLL 28U +#define NODE_RPLL 29U +#define NODE_IOPLL 30U #define NODE_MIN NODE_APU -#define NODE_MAX NODE_SATA +#define NODE_MAX NODE_IOPLL /* Request acknowledge argument values */ #define REQUEST_ACK_NO 1U diff --git a/lib/sw_apps/zynqmp_pmufw/src/pm_master.c b/lib/sw_apps/zynqmp_pmufw/src/pm_master.c index 222cbc71..ed4aa4eb 100644 --- a/lib/sw_apps/zynqmp_pmufw/src/pm_master.c +++ b/lib/sw_apps/zynqmp_pmufw/src/pm_master.c @@ -40,6 +40,7 @@ #include "pm_defs.h" #include "pm_sram.h" #include "pm_usb.h" +#include "pm_pll.h" #include "pm_periph.h" #include "pm_callbacks.h" #include "ipi_buffer.h" @@ -118,6 +119,46 @@ PmRequirement pmApuReq_g[PM_MASTER_APU_SLAVE_MAX] = { .currReq = 0U, .nextReq = 0U, }, + [PM_MASTER_APU_SLAVE_APLL] = { + .slave = &pmSlaveApll_g.slv, + .requestor = &pmMasterApu_g, + .info = 0U, + .defaultReq = 0U, + .currReq = 0U, + .nextReq = 0U, + }, + [PM_MASTER_APU_SLAVE_VPLL] = { + .slave = &pmSlaveVpll_g.slv, + .requestor = &pmMasterApu_g, + .info = 0U, + .defaultReq = 0U, + .currReq = 0U, + .nextReq = 0U, + }, + [PM_MASTER_APU_SLAVE_DPLL] = { + .slave = &pmSlaveDpll_g.slv, + .requestor = &pmMasterApu_g, + .info = 0U, + .defaultReq = 0U, + .currReq = 0U, + .nextReq = 0U, + }, + [PM_MASTER_APU_SLAVE_RPLL] = { + .slave = &pmSlaveRpll_g.slv, + .requestor = &pmMasterApu_g, + .info = 0U, + .defaultReq = 0U, + .currReq = 0U, + .nextReq = 0U, + }, + [PM_MASTER_APU_SLAVE_IOPLL] = { + .slave = &pmSlaveIOpll_g.slv, + .requestor = &pmMasterApu_g, + .info = 0U, + .defaultReq = 0U, + .currReq = 0U, + .nextReq = 0U, + }, }; /* Requirement of RPU_0 master */ @@ -218,6 +259,46 @@ PmRequirement pmRpu0Req_g[PM_MASTER_RPU_0_SLAVE_MAX] = { .currReq = 0U, .nextReq = 0U, }, + [PM_MASTER_RPU_0_SLAVE_APLL] = { + .slave = &pmSlaveApll_g.slv, + .requestor = &pmMasterRpu0_g, + .info = 0U, + .defaultReq = 0U, + .currReq = 0U, + .nextReq = 0U, + }, + [PM_MASTER_RPU_0_SLAVE_VPLL] = { + .slave = &pmSlaveVpll_g.slv, + .requestor = &pmMasterRpu0_g, + .info = 0U, + .defaultReq = 0U, + .currReq = 0U, + .nextReq = 0U, + }, + [PM_MASTER_RPU_0_SLAVE_DPLL] = { + .slave = &pmSlaveDpll_g.slv, + .requestor = &pmMasterRpu0_g, + .info = 0U, + .defaultReq = 0U, + .currReq = 0U, + .nextReq = 0U, + }, + [PM_MASTER_RPU_0_SLAVE_RPLL] = { + .slave = &pmSlaveRpll_g.slv, + .requestor = &pmMasterRpu0_g, + .info = 0U, + .defaultReq = 0U, + .currReq = 0U, + .nextReq = 0U, + }, + [PM_MASTER_RPU_0_SLAVE_IOPLL] = { + .slave = &pmSlaveIOpll_g.slv, + .requestor = &pmMasterRpu0_g, + .info = 0U, + .defaultReq = 0U, + .currReq = 0U, + .nextReq = 0U, + }, }; PmMaster pmMasterApu_g = { diff --git a/lib/sw_apps/zynqmp_pmufw/src/pm_master.h b/lib/sw_apps/zynqmp_pmufw/src/pm_master.h index 2202e0a7..7e37e0b1 100644 --- a/lib/sw_apps/zynqmp_pmufw/src/pm_master.h +++ b/lib/sw_apps/zynqmp_pmufw/src/pm_master.h @@ -64,7 +64,13 @@ typedef struct PmMaster PmMaster; #define PM_MASTER_APU_SLAVE_USB1 6U #define PM_MASTER_APU_SLAVE_TTC0 7U #define PM_MASTER_APU_SLAVE_SATA 8U -#define PM_MASTER_APU_SLAVE_MAX 9U +#define PM_MASTER_APU_SLAVE_APLL 9U +#define PM_MASTER_APU_SLAVE_VPLL 10U +#define PM_MASTER_APU_SLAVE_DPLL 11U +#define PM_MASTER_APU_SLAVE_RPLL 12U +#define PM_MASTER_APU_SLAVE_IOPLL 13U + +#define PM_MASTER_APU_SLAVE_MAX 14U /* Rpu0 slaves */ #define PM_MASTER_RPU_0_SLAVE_TCM0A 0U @@ -79,7 +85,13 @@ typedef struct PmMaster PmMaster; #define PM_MASTER_RPU_0_SLAVE_USB1 9U #define PM_MASTER_RPU_0_SLAVE_TTC0 10U #define PM_MASTER_RPU_0_SLAVE_SATA 11U -#define PM_MASTER_RPU_0_SLAVE_MAX 12U +#define PM_MASTER_RPU_0_SLAVE_APLL 12U +#define PM_MASTER_RPU_0_SLAVE_VPLL 13U +#define PM_MASTER_RPU_0_SLAVE_DPLL 14U +#define PM_MASTER_RPU_0_SLAVE_RPLL 15U +#define PM_MASTER_RPU_0_SLAVE_IOPLL 16U + +#define PM_MASTER_RPU_0_SLAVE_MAX 17U /* Pm Master request info masks */ #define PM_MASTER_WAKEUP_REQ_MASK 0x1U diff --git a/lib/sw_apps/zynqmp_pmufw/src/pm_node.c b/lib/sw_apps/zynqmp_pmufw/src/pm_node.c index daac47c0..f4c3b67f 100644 --- a/lib/sw_apps/zynqmp_pmufw/src/pm_node.c +++ b/lib/sw_apps/zynqmp_pmufw/src/pm_node.c @@ -39,6 +39,7 @@ #include "pm_sram.h" #include "pm_usb.h" #include "pm_periph.h" +#include "pm_pll.h" static PmNode* const pmNodes[NODE_MAX] = { &pmApuProcs_g[PM_PROC_APU_0].node, @@ -63,6 +64,11 @@ static PmNode* const pmNodes[NODE_MAX] = { &pmSlaveUsb1_g.slv.node, &pmSlaveTtc0_g.slv.node, &pmSlaveSata_g.slv.node, + &pmSlaveApll_g.slv.node, + &pmSlaveVpll_g.slv.node, + &pmSlaveDpll_g.slv.node, + &pmSlaveRpll_g.slv.node, + &pmSlaveIOpll_g.slv.node, }; /** diff --git a/lib/sw_apps/zynqmp_pmufw/src/pm_node.h b/lib/sw_apps/zynqmp_pmufw/src/pm_node.h index 3ea0be3f..b9df49b8 100644 --- a/lib/sw_apps/zynqmp_pmufw/src/pm_node.h +++ b/lib/sw_apps/zynqmp_pmufw/src/pm_node.h @@ -65,6 +65,7 @@ typedef int (*const PmNodeTranHandler)(PmNode* const nodePtr); #define PM_TYPE_GPU_PP (PM_TYPE_SLAVE + 2U) #define PM_TYPE_TTC (PM_TYPE_SLAVE + 3U) #define PM_TYPE_SATA (PM_TYPE_SLAVE + 4U) +#define PM_TYPE_PLL (PM_TYPE_SLAVE + 5U) #define IS_PROC(type) (PM_TYPE_PROC == type) diff --git a/lib/sw_apps/zynqmp_pmufw/src/pm_pll.c b/lib/sw_apps/zynqmp_pmufw/src/pm_pll.c new file mode 100644 index 00000000..8e838f22 --- /dev/null +++ b/lib/sw_apps/zynqmp_pmufw/src/pm_pll.c @@ -0,0 +1,370 @@ +/* + * 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 THE + * XILINX CONSORTIUM 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. + */ + +/********************************************************************* + * Contains: + * - PLL slave implementation + * - PLL slave's FSM implementation - only tracks the PLL usage status + * (used/unused). + * - Functions for saving and restoring PLLs' context + * + * Note: PMU does not control states of PLLs. When none of FPD PLLs + * is used and FPD is going to be powered down, PMU saves context of + * PLLs in FPD and asserts their reset. After powering up FPD, PMU + * restores the state of PLL based on saved context only when PLL is + * needed for the use. + *********************************************************************/ + +#include "pm_pll.h" +#include "pm_master.h" +#include "pm_power.h" +#include "crf_apb.h" +#include "crl_apb.h" +#include "xpfw_util.h" + +/* PLL states */ +static const u32 pmPllStates[PM_PLL_STATE_MAX] = { + [PM_PLL_STATE_UNUSED] = 0U, + [PM_PLL_STATE_USED] = PM_CAP_ACCESS | PM_CAP_POWER, +}; + +/* PLL transition table (from which to which state PLL can transit) */ +static const PmStateTran pmPllTransitions[] = { + { + .fromState = PM_PLL_STATE_USED, + .toState = PM_PLL_STATE_UNUSED, + }, { + .fromState = PM_PLL_STATE_UNUSED, + .toState = PM_PLL_STATE_USED, + }, +}; + +/** + * PmPllBypassAndReset() - Bypass and reset/power down a PLL + * @pll Pointer to a Pll to be bypassed/reset + */ +static void PmPllBypassAndReset(PmSlavePll* const pll) +{ + /* Bypass PLL before putting it into the reset */ + XPfw_RMW32(pll->addr + PM_PLL_CTRL_OFFSET, PM_PLL_CTRL_BYPASS_MASK, + PM_PLL_CTRL_BYPASS_MASK); + + /* Power down PLL (= reset PLL) */ + XPfw_RMW32(pll->addr + PM_PLL_CTRL_OFFSET, PM_PLL_CTRL_RESET_MASK, + PM_PLL_CTRL_RESET_MASK); +} + +/** + * PmPllSuspend() - Save context of PLL and power it down (reset) + * @pll Pointer to a Pll to be suspended + */ +static void PmPllSuspend(PmSlavePll* const pll) +{ + u32 val; + + /* Save register setting */ + pll->context.ctrl = XPfw_Read32(pll->addr + PM_PLL_CTRL_OFFSET); + pll->context.cfg = XPfw_Read32(pll->addr + PM_PLL_CFG_OFFSET); + pll->context.frac = XPfw_Read32(pll->addr + PM_PLL_FRAC_OFFSET); + pll->context.toCtrl = XPfw_Read32(pll->toCtrlAddr); + pll->context.saved = true; + + val = XPfw_Read32(pll->addr + PM_PLL_CTRL_OFFSET); + /* If PLL is not already in reset, bypass it and put in reset/pwrdn */ + if (0U == (val & PM_PLL_CTRL_RESET_MASK)) { + PmPllBypassAndReset(pll); + } +} + +/** + * PmPllResume() - Restore PLL context + * @pll Pll whose context should be restored + * + * @return Status of resume: + * - XST_SUCCESS if resumed correctly + * - XST_FAILURE if resume failed (if PLL failed to lock) + */ +static int PmPllResume(PmSlavePll* const pll) +{ + int status = XST_SUCCESS; + + if (true == pll->context.saved) { + /* Restore register values with reset and bypass asserted */ + XPfw_Write32(pll->addr + PM_PLL_CTRL_OFFSET, pll->context.ctrl | + PM_PLL_CTRL_RESET_MASK | PM_PLL_CTRL_BYPASS_MASK); + XPfw_Write32(pll->addr + PM_PLL_CFG_OFFSET, pll->context.cfg); + XPfw_Write32(pll->addr + PM_PLL_FRAC_OFFSET, pll->context.frac); + XPfw_Write32(pll->toCtrlAddr, pll->context.toCtrl); + pll->context.saved = false; + } + + if (0U != (PM_PLL_CTRL_RESET_MASK & pll->context.ctrl)) { + /* By saved/init configuration PLL is in reset, leave it as is */ + goto done; + } + + /* Release reset */ + XPfw_RMW32(pll->addr + PM_PLL_CTRL_OFFSET, PM_PLL_CTRL_RESET_MASK, + ~PM_PLL_CTRL_RESET_MASK); + /* Poll status register for the lock */ + status = XPfw_UtilPollForMask(pll->statusAddr, pll->lockMask, + PM_PLL_LOCK_TIMEOUT); + if (XST_SUCCESS != status) { + /* Failed to lock PLL - assert reset and return */ + XPfw_RMW32(pll->addr + PM_PLL_CTRL_OFFSET, + PM_PLL_CTRL_RESET_MASK, PM_PLL_CTRL_RESET_MASK); + goto done; + } + + /* PLL is bypassed here (done by the reset) */ + if (0U == (PM_PLL_CTRL_BYPASS_MASK & pll->context.ctrl)) { + /* According to saved context PLL should not be bypassed */ + XPfw_RMW32(pll->addr + PM_PLL_CTRL_OFFSET, + PM_PLL_CTRL_BYPASS_MASK, + ~PM_PLL_CTRL_BYPASS_MASK); + } + +done: + return status; +} + +/** + * PmPllFsmHandler() - PLL FSM handler + * @slave Slave whose state should be changed + * @nextState State the slave should enter + * + * @return Always XST_SUCCESS if FSM is implemented correctly + * + * Note: PLL FSM basically updates currState variable and restores PLL state + * if needed. FSM transitions cannot fail. + */ +static int PmPllFsmHandler(PmSlave* const slave, const PmStateId nextState) +{ + int status; + PmSlavePll* pll = (PmSlavePll*)slave->node.derived; + + switch (nextState) { + case PM_PLL_STATE_USED: + /* Resume the PLL */ + status = PmPllResume(pll); + break; + case PM_PLL_STATE_UNUSED: + /* Bypassing and reseting PLL cannot fail */ + PmPllBypassAndReset(pll); + status = XST_SUCCESS; + break; + default: + status = XST_PM_INTERNAL; + break; + } + + if (XST_SUCCESS == status) { + slave->node.currState = nextState; + } + + return status; +} + +/* PLL FSM */ +static const PmSlaveFsm slavePllFsm = { + .states = pmPllStates, + .statesCnt = ARRAY_SIZE(pmPllStates), + .trans = pmPllTransitions, + .transCnt = ARRAY_SIZE(pmPllTransitions), + .enterState = PmPllFsmHandler, +}; + +/* APLL */ +static PmRequirement* const pmApllReqs[] = { + &pmApuReq_g[PM_MASTER_APU_SLAVE_APLL], + &pmRpu0Req_g[PM_MASTER_RPU_0_SLAVE_APLL], +}; + +PmSlavePll pmSlaveApll_g = { + .slv = { + .node = { + .derived = &pmSlaveApll_g, + .nodeId = NODE_APLL, + .typeId = PM_TYPE_PLL, + .parent = &pmPowerDomainFpd_g, + .ops = NULL, + }, + .reqs = pmApllReqs, + .reqsCnt = ARRAY_SIZE(pmApllReqs), + .wake = NULL, + .slvFsm = &slavePllFsm, + }, + .context = { + .saved = false, + }, + .addr = CRF_APB_APLL_CTRL, + .toCtrlAddr = CRF_APB_APLL_TO_LPD_CTRL, + .statusAddr = CRF_APB_PLL_STATUS, + .lockMask = CRF_APB_PLL_STATUS_APLL_LOCK_MASK, +}; + +/* VPLL */ +static PmRequirement* const pmVpllReqs[] = { + &pmApuReq_g[PM_MASTER_APU_SLAVE_VPLL], + &pmRpu0Req_g[PM_MASTER_RPU_0_SLAVE_VPLL], +}; + +PmSlavePll pmSlaveVpll_g = { + .slv = { + .node = { + .derived = &pmSlaveVpll_g, + .nodeId = NODE_VPLL, + .typeId = PM_TYPE_PLL, + .parent = &pmPowerDomainFpd_g, + .ops = NULL, + }, + .reqs = pmVpllReqs, + .reqsCnt = ARRAY_SIZE(pmVpllReqs), + .wake = NULL, + .slvFsm = &slavePllFsm, + }, + .context = { + .saved = false, + }, + .addr = CRF_APB_VPLL_CTRL, + .toCtrlAddr = CRF_APB_VPLL_TO_LPD_CTRL, + .statusAddr = CRF_APB_PLL_STATUS, + .lockMask = CRF_APB_PLL_STATUS_VPLL_LOCK_MASK, +}; + +/* DPLL */ +static PmRequirement* const pmDpllReqs[] = { + &pmApuReq_g[PM_MASTER_APU_SLAVE_DPLL], + &pmRpu0Req_g[PM_MASTER_RPU_0_SLAVE_DPLL], +}; + +PmSlavePll pmSlaveDpll_g = { + .slv = { + .node = { + .derived = &pmSlaveDpll_g, + .nodeId = NODE_DPLL, + .typeId = PM_TYPE_PLL, + .parent = &pmPowerDomainFpd_g, + .ops = NULL, + }, + .reqs = pmDpllReqs, + .reqsCnt = ARRAY_SIZE(pmDpllReqs), + .wake = NULL, + .slvFsm = &slavePllFsm, + }, + .context = { + .saved = false, + }, + .addr = CRF_APB_DPLL_CTRL, + .toCtrlAddr = CRF_APB_DPLL_TO_LPD_CTRL, + .statusAddr = CRF_APB_PLL_STATUS, + .lockMask = CRF_APB_PLL_STATUS_DPLL_LOCK_MASK, +}; + +/* RPLL */ +static PmRequirement* const pmRpllReqs[] = { + &pmApuReq_g[PM_MASTER_APU_SLAVE_RPLL], + &pmRpu0Req_g[PM_MASTER_RPU_0_SLAVE_RPLL], +}; + +PmSlavePll pmSlaveRpll_g = { + .slv = { + .node = { + .derived = &pmSlaveRpll_g, + .nodeId = NODE_RPLL, + .typeId = PM_TYPE_PLL, + .parent = NULL, + .ops = NULL, + }, + .reqs = pmRpllReqs, + .reqsCnt = ARRAY_SIZE(pmRpllReqs), + .wake = NULL, + .slvFsm = &slavePllFsm, + }, + .context = { + .saved = false, + }, + .addr = CRL_APB_RPLL_CTRL, + .toCtrlAddr = CRL_APB_RPLL_TO_FPD_CTRL, + .statusAddr = CRL_APB_PLL_STATUS, + .lockMask = CRL_APB_PLL_STATUS_RPLL_LOCK_MASK, +}; + +/* IOPLL */ +static PmRequirement* const pmIOpllReqs[] = { + &pmApuReq_g[PM_MASTER_APU_SLAVE_IOPLL], + &pmRpu0Req_g[PM_MASTER_RPU_0_SLAVE_IOPLL], +}; + +PmSlavePll pmSlaveIOpll_g = { + .slv = { + .node = { + .derived = &pmSlaveIOpll_g, + .nodeId = NODE_IOPLL, + .typeId = PM_TYPE_PLL, + .parent = NULL, + .ops = NULL, + }, + .reqs = pmIOpllReqs, + .reqsCnt = ARRAY_SIZE(pmIOpllReqs), + .wake = NULL, + .slvFsm = &slavePllFsm, + }, + .context = { + .saved = false, + }, + .addr = CRL_APB_IOPLL_CTRL, + .toCtrlAddr = CRL_APB_IOPLL_TO_FPD_CTRL, + .statusAddr = CRL_APB_PLL_STATUS, + .lockMask = CRL_APB_PLL_STATUS_IOPLL_LOCK_MASK, +}; + +static PmSlavePll* const pmPlls[] = { + &pmSlaveApll_g, + &pmSlaveVpll_g, + &pmSlaveDpll_g, + &pmSlaveRpll_g, + &pmSlaveIOpll_g, +}; + +/** + * PmPllSuspendAll() - Suspend all PLLs whose power parent is given as argument + * @powerParent Power parent of PLLs to be suspended + */ +void PmPllSuspendAll(const PmPower* const powerParent) +{ + u8 i; + + for (i = 0U; i < ARRAY_SIZE(pmPlls); i++) { + if (powerParent == pmPlls[i]->slv.node.parent) { + PmPllSuspend(pmPlls[i]); + } + } +} diff --git a/lib/sw_apps/zynqmp_pmufw/src/pm_pll.h b/lib/sw_apps/zynqmp_pmufw/src/pm_pll.h new file mode 100644 index 00000000..32336348 --- /dev/null +++ b/lib/sw_apps/zynqmp_pmufw/src/pm_pll.h @@ -0,0 +1,136 @@ +/* + * 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 THE + * XILINX CONSORTIUM 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. + */ + +/********************************************************************* + * Contains: + * - PLL slave implementation + * - Functions for saving and restoring PLLs' context + * + * Note: PMU does not control states of PLLs. When none of FPD PLLs + * is used and FPD is going to be powered down, PMU saves context of + * PLLs in FPD and asserts their reset. After powering up FPD, PMU + * restores the state of PLL based on saved context. + *********************************************************************/ + +#ifndef PM_PLL_H_ +#define PM_PLL_H_ + +#include "pm_slave.h" + +/********************************************************************* + * Macros + ********************************************************************/ +/* PLL states: */ +/* + * PLL is not used by any master, so it can be powered down and it's power + * parent can be powered down as well. + */ +#define PM_PLL_STATE_UNUSED 0U +/* PLL is used by at least one master which is controlling state of PLL */ +#define PM_PLL_STATE_USED 1U +#define PM_PLL_STATE_MAX 2U + +/* Register offsets (in regard to PLL's base address of control registers) */ +#define PM_PLL_CTRL_OFFSET 0x0U +#define PM_PLL_CFG_OFFSET 0x4U +#define PM_PLL_FRAC_OFFSET 0x8U + +/* Masks of bitfields in PLL's control register */ +#define PM_PLL_CTRL_RESET_MASK 0x1U +#define PM_PLL_CTRL_BYPASS_MASK 0x8U + +/* Configurable: timeout period when waiting for PLL to lock */ +#define PM_PLL_LOCK_TIMEOUT 0x10000U +/********************************************************************* + * Structure definitions + ********************************************************************/ +/** + * PmPllContext - Structure for saving context of PLL registers. + * Contains variable to store default content of: + * @ctrl Control register + * @cfg Configuration register + * @frac Fractional control register + * @toCtrl Control for a cross domain (a divisor) + * @saved Flag stating are variables of this structure containing values + * to be restored or not + * + * Note: context of the PLL is saved just before power-parent of PLL gets + * powered down (FPD for example) and then the 'saved' flag is set to true. + * In order to enter PM_PLL_STATE_USED state, PLL must have valid context, + * meaning the 'saved' flag must be false. Upon initialization (as long as + * power-parent does not get powered down), all data except 'saved' is + * invalid/not-initialized (basically 'saved' flag also states do fields of + * this structure have valid values or not). + */ +typedef struct PmPllContext { + u32 ctrl; + u32 cfg; + u32 frac; + u32 toCtrl; + bool saved; +} PmPllContext; + +/** + * PmSlavePll - Structure used for Pll slave + * @slv Base slave structure + * @context Data to store context of the PLL - if after boot PLL has no + * context, it should not be initially locked by PMU, but by a + * master. To inform PMU that initially PLL has no context, this + * field should be initialized with the PM_PLL_CTRL_RESET_MASK + * set, statically or through PCW. + * @addr Base address of the PLL's control registers + * @toCtrlAddr Absolute address of cross-domain control register + * @statusAddr Address of the PLL's status register + * @lockMask Mask of the lock in status register + */ +typedef struct PmSlavePll { + PmSlave slv; + PmPllContext context; + const u32 addr; + const u32 toCtrlAddr; + const u32 statusAddr; + const u32 lockMask; +} PmSlavePll; + +/********************************************************************* + * Global data declarations + ********************************************************************/ +extern PmSlavePll pmSlaveApll_g; +extern PmSlavePll pmSlaveDpll_g; +extern PmSlavePll pmSlaveVpll_g; +extern PmSlavePll pmSlaveRpll_g; +extern PmSlavePll pmSlaveIOpll_g; + +/********************************************************************* + * Function declarations + ********************************************************************/ +void PmPllSuspendAll(const PmPower* const powerParent); + +#endif diff --git a/lib/sw_apps/zynqmp_pmufw/src/pm_power.c b/lib/sw_apps/zynqmp_pmufw/src/pm_power.c index 31eda68a..73dd7420 100644 --- a/lib/sw_apps/zynqmp_pmufw/src/pm_power.c +++ b/lib/sw_apps/zynqmp_pmufw/src/pm_power.c @@ -39,6 +39,7 @@ #include "pm_master.h" #include "pm_sram.h" #include "pm_periph.h" +#include "pm_pll.h" #include "xpfw_rom_interface.h" #include "crf_apb.h" @@ -105,6 +106,7 @@ static int PmPowerDownFpd(void) int status = XpbrRstFpdHandler(); if (XST_SUCCESS == status) { + PmPllSuspendAll(&pmPowerDomainFpd_g); PmCrfSaveContext(); status = XpbrPwrDnFpdHandler(); /* @@ -235,6 +237,9 @@ static PmNode* pmFpdChildren[] = { &pmPowerIslandApu_g.node, &pmSlaveL2_g.slv.node, &pmSlaveSata_g.slv.node, + &pmSlaveApll_g.slv.node, + &pmSlaveVpll_g.slv.node, + &pmSlaveDpll_g.slv.node, }; /* Operations for the Rpu power island */ diff --git a/lib/sw_apps/zynqmp_pmufw/src/pm_slave.c b/lib/sw_apps/zynqmp_pmufw/src/pm_slave.c index b57e9199..41088667 100644 --- a/lib/sw_apps/zynqmp_pmufw/src/pm_slave.c +++ b/lib/sw_apps/zynqmp_pmufw/src/pm_slave.c @@ -40,6 +40,7 @@ #include "pm_sram.h" #include "pm_usb.h" #include "pm_periph.h" +#include "pm_pll.h" #include "pm_power.h" #include "lpd_slcr.h" @@ -82,6 +83,11 @@ static PmSlave* const pmSlaves[] = { &pmSlaveUsb1_g.slv, &pmSlaveTtc0_g.slv, &pmSlaveSata_g.slv, + &pmSlaveApll_g.slv, + &pmSlaveVpll_g.slv, + &pmSlaveDpll_g.slv, + &pmSlaveRpll_g.slv, + &pmSlaveIOpll_g.slv, }; /** diff --git a/lib/sw_apps/zynqmp_pmufw/src/xpfw_version.h b/lib/sw_apps/zynqmp_pmufw/src/xpfw_version.h index bec37440..3699af78 100644 --- a/lib/sw_apps/zynqmp_pmufw/src/xpfw_version.h +++ b/lib/sw_apps/zynqmp_pmufw/src/xpfw_version.h @@ -1,4 +1,4 @@ #ifndef ZYNQMP_XPFW_VERSION__H_ #define ZYNQMP_XPFW_VERSION__H_ - #define ZYNQMP_XPFW_VERSION "2015.1-swbeta2-40-ge46a2aec66e8" + #define ZYNQMP_XPFW_VERSION "2015.1-swbeta2-41-g884008482155" #endif