historical/m0-applesillicon.git/xnu-qemu-arm64-5.1.0/roms/u-boot/arch/arm/mach-keystone/psc.c
2024-01-16 11:20:27 -06:00

338 lines
9.6 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Keystone: PSC configuration module
*
* (C) Copyright 2012-2014
* Texas Instruments Incorporated, <www.ti.com>
*/
#include <common.h>
#include <linux/errno.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/arch/psc_defs.h>
/**
* psc_delay() - delay for psc
*
* Return: 10
*/
int psc_delay(void)
{
udelay(10);
return 10;
}
/**
* psc_wait() - Wait for end of transitional state
* @domain_num: GPSC domain number
*
* Polls pstat for the selected domain and waits for transitions to be complete.
* Since this is boot loader code it is *ASSUMED* that interrupts are disabled
* and no other core is mucking around with the psc at the same time.
*
* Return: 0 when the domain is free. Returns -1 if a timeout occurred waiting
* for the completion.
*/
int psc_wait(u32 domain_num)
{
u32 retry;
u32 ptstat;
/*
* Do nothing if the power domain is in transition. This should never
* happen since the boot code is the only software accesses psc.
* It's still remotely possible that the hardware state machines
* initiate transitions.
* Don't trap if the domain (or a module in this domain) is
* stuck in transition.
*/
retry = 0;
do {
ptstat = __raw_readl(KS2_PSC_BASE + PSC_REG_PSTAT);
ptstat = ptstat & (1 << domain_num);
} while ((ptstat != 0) && ((retry += psc_delay()) <
PSC_PTSTAT_TIMEOUT_LIMIT));
if (retry >= PSC_PTSTAT_TIMEOUT_LIMIT)
return -1;
return 0;
}
/**
* psc_get_domain_num() - Get the domain number
* @mod_num: LPSC module number
*/
u32 psc_get_domain_num(u32 mod_num)
{
u32 domain_num;
/* Get the power domain associated with the module number */
domain_num = __raw_readl(KS2_PSC_BASE + PSC_REG_MDCFG(mod_num));
domain_num = PSC_REG_MDCFG_GET_PD(domain_num);
return domain_num;
}
/**
* psc_set_state() - powers up/down a module
* @mod_num: LPSC module number
* @state: 1 to enable, 0 to disable.
*
* Powers up/down the requested module and the associated power domain if
* required. No action is taken it the module is already powered up/down.
* This only controls modules. The domain in which the module resides will
* be left in the power on state. Multiple modules can exist in a power
* domain, so powering down the domain based on a single module is not done.
*
* Return: 0 on success, -1 if the module can't be powered up, or if there is a
* timeout waiting for the transition.
*/
int psc_set_state(u32 mod_num, u32 state)
{
u32 domain_num;
u32 pdctl;
u32 mdctl;
u32 ptcmd;
u32 reset_iso;
u32 v;
/*
* Get the power domain associated with the module number, and reset
* isolation functionality
*/
v = __raw_readl(KS2_PSC_BASE + PSC_REG_MDCFG(mod_num));
domain_num = PSC_REG_MDCFG_GET_PD(v);
reset_iso = PSC_REG_MDCFG_GET_RESET_ISO(v);
/* Wait for the status of the domain/module to be non-transitional */
if (psc_wait(domain_num) != 0)
return -1;
/*
* Perform configuration even if the current status matches the
* existing state
*
* Set the next state of the power domain to on. It's OK if the domain
* is always on. This code will not ever power down a domain, so no
* change is made if the new state is power down.
*/
if (state == PSC_REG_VAL_MDCTL_NEXT_ON) {
pdctl = __raw_readl(KS2_PSC_BASE + PSC_REG_PDCTL(domain_num));
pdctl = PSC_REG_PDCTL_SET_NEXT(pdctl,
PSC_REG_VAL_PDCTL_NEXT_ON);
__raw_writel(pdctl, KS2_PSC_BASE + PSC_REG_PDCTL(domain_num));
}
/* Set the next state for the module to enabled/disabled */
mdctl = __raw_readl(KS2_PSC_BASE + PSC_REG_MDCTL(mod_num));
mdctl = PSC_REG_MDCTL_SET_NEXT(mdctl, state);
mdctl = PSC_REG_MDCTL_SET_RESET_ISO(mdctl, reset_iso);
__raw_writel(mdctl, KS2_PSC_BASE + PSC_REG_MDCTL(mod_num));
/* Trigger the enable */
ptcmd = __raw_readl(KS2_PSC_BASE + PSC_REG_PTCMD);
ptcmd |= (u32)(1<<domain_num);
__raw_writel(ptcmd, KS2_PSC_BASE + PSC_REG_PTCMD);
/* Wait on the complete */
return psc_wait(domain_num);
}
/**
* psc_enable_module() - power up a module
* @mod_num: LPSC module number
*
* Powers up the requested module and the associated power domain
* if required. No action is taken it the module is already powered up.
*
* Return: 0 on success, -1 if the module can't be powered up, or
* if there is a timeout waiting for the transition.
*
*/
int psc_enable_module(u32 mod_num)
{
u32 mdctl;
/* Set the bit to apply reset */
mdctl = __raw_readl(KS2_PSC_BASE + PSC_REG_MDCTL(mod_num));
if ((mdctl & 0x3f) == PSC_REG_VAL_MDSTAT_STATE_ON)
return 0;
return psc_set_state(mod_num, PSC_REG_VAL_MDCTL_NEXT_ON);
}
/**
* psc_disable_module() - Power down a module
* @mod_num: LPSC module number
*
* Return: 0 on success, -1 on failure or timeout.
*/
int psc_disable_module(u32 mod_num)
{
u32 mdctl;
/* Set the bit to apply reset */
mdctl = __raw_readl(KS2_PSC_BASE + PSC_REG_MDCTL(mod_num));
if ((mdctl & 0x3f) == 0)
return 0;
mdctl = PSC_REG_MDCTL_SET_LRSTZ(mdctl, 0);
__raw_writel(mdctl, KS2_PSC_BASE + PSC_REG_MDCTL(mod_num));
return psc_set_state(mod_num, PSC_REG_VAL_MDCTL_NEXT_SWRSTDISABLE);
}
/**
* psc_set_reset_iso() - Set the reset isolation bit in mdctl
* @mod_num: LPSC module number
*
* The reset isolation enable bit is set. The state of the module is not
* changed.
*
* Return: 0 if the module config showed that reset isolation is supported.
* Returns 1 otherwise. This is not an error, but setting the bit in mdctl
* has no effect.
*/
int psc_set_reset_iso(u32 mod_num)
{
u32 v;
u32 mdctl;
/* Set the reset isolation bit */
mdctl = __raw_readl(KS2_PSC_BASE + PSC_REG_MDCTL(mod_num));
mdctl = PSC_REG_MDCTL_SET_RESET_ISO(mdctl, 1);
__raw_writel(mdctl, KS2_PSC_BASE + PSC_REG_MDCTL(mod_num));
v = __raw_readl(KS2_PSC_BASE + PSC_REG_MDCFG(mod_num));
if (PSC_REG_MDCFG_GET_RESET_ISO(v) == 1)
return 0;
return 1;
}
/**
* psc_disable_domain() - Disable a power domain
* @domain_num: GPSC domain number
*/
int psc_disable_domain(u32 domain_num)
{
u32 pdctl;
u32 ptcmd;
pdctl = __raw_readl(KS2_PSC_BASE + PSC_REG_PDCTL(domain_num));
pdctl = PSC_REG_PDCTL_SET_NEXT(pdctl, PSC_REG_VAL_PDCTL_NEXT_OFF);
pdctl = PSC_REG_PDCTL_SET_PDMODE(pdctl, PSC_REG_VAL_PDCTL_PDMODE_SLEEP);
__raw_writel(pdctl, KS2_PSC_BASE + PSC_REG_PDCTL(domain_num));
ptcmd = __raw_readl(KS2_PSC_BASE + PSC_REG_PTCMD);
ptcmd |= (u32)(1 << domain_num);
__raw_writel(ptcmd, KS2_PSC_BASE + PSC_REG_PTCMD);
return psc_wait(domain_num);
}
/**
* psc_module_keep_in_reset_enabled() - Keep module in enabled,in-reset state
* @mod_num: LPSC module number
* @gate_clocks: Can the clocks be gated on this module?
*
* Enable the module, but do not release the module from local reset. This is
* necessary for many processor systems on keystone SoCs to allow for system
* initialization from a master processor prior to releasing the processor
* from reset.
*/
int psc_module_keep_in_reset_enabled(u32 mod_num, bool gate_clocks)
{
u32 mdctl, ptcmd, mdstat;
u32 next_state;
int domain_num = psc_get_domain_num(mod_num);
int timeout = 100000;
/* Wait for any previous transitions to complete */
psc_wait(domain_num);
mdctl = __raw_readl(KS2_PSC_BASE + PSC_REG_MDCTL(mod_num));
/* Should be set 0 to assert Local reset */
if ((mdctl & PSC_REG_MDCTL_SET_LRSTZ(mdctl, 1))) {
mdctl = PSC_REG_MDCTL_SET_LRSTZ(mdctl, 0);
__raw_writel(mdctl, KS2_PSC_BASE + PSC_REG_MDCTL(mod_num));
/* Wait for transition to take place */
psc_wait(domain_num);
}
/* Clear Module reset */
mdctl = __raw_readl(KS2_PSC_BASE + PSC_REG_MDCTL(mod_num));
next_state = gate_clocks ? PSC_REG_VAL_MDCTL_NEXT_OFF :
PSC_REG_VAL_MDCTL_NEXT_ON;
mdctl = PSC_REG_MDCTL_SET_NEXT(mdctl, next_state);
__raw_writel(mdctl, KS2_PSC_BASE + PSC_REG_MDCTL(mod_num));
/* Trigger PD transition */
ptcmd = __raw_readl(KS2_PSC_BASE + PSC_REG_PTCMD);
ptcmd |= (u32)(1 << domain_num);
__raw_writel(ptcmd, KS2_PSC_BASE + PSC_REG_PTCMD);
psc_wait(domain_num);
mdstat = __raw_readl(KS2_PSC_BASE + PSC_REG_MDSTAT(mod_num));
while (timeout) {
mdstat = __raw_readl(KS2_PSC_BASE + PSC_REG_MDSTAT(mod_num));
if (!(PSC_REG_MDSTAT_GET_STATUS(mdstat) & 0x30) &&
PSC_REG_MDSTAT_GET_MRSTDONE(mdstat) &&
PSC_REG_MDSTAT_GET_LRSTDONE(mdstat))
break;
timeout--;
}
if (!timeout) {
printf("%s: Timedout waiting for mdstat(0x%08x) to change\n",
__func__, mdstat);
return -ETIMEDOUT;
}
return 0;
}
/**
* psc_module_release_from_reset() - Release the module from reset
* @mod_num: LPSC module number
*
* This is the follow through for the command 'psc_module_keep_in_reset_enabled'
* Allowing the module to be released from reset once all required inits are
* complete for the module. Typically, this allows the processor module to start
* execution.
*/
int psc_module_release_from_reset(u32 mod_num)
{
u32 mdctl, mdstat;
int domain_num = psc_get_domain_num(mod_num);
int timeout = 100000;
/* Wait for any previous transitions to complete */
psc_wait(domain_num);
mdctl = __raw_readl(KS2_PSC_BASE + PSC_REG_MDCTL(mod_num));
/* Should be set to 1 to de-assert Local reset */
if ((mdctl & PSC_REG_MDCTL_SET_LRSTZ(mdctl, 0))) {
mdctl = PSC_REG_MDCTL_SET_LRSTZ(mdctl, 1);
__raw_writel(mdctl, KS2_PSC_BASE + PSC_REG_MDCTL(mod_num));
/* Wait for transition to take place */
psc_wait(domain_num);
}
mdstat = __raw_readl(KS2_PSC_BASE + PSC_REG_MDSTAT(mod_num));
while (timeout) {
mdstat = __raw_readl(KS2_PSC_BASE + PSC_REG_MDSTAT(mod_num));
if (!(PSC_REG_MDSTAT_GET_STATUS(mdstat) & 0x30) &&
PSC_REG_MDSTAT_GET_MRSTDONE(mdstat) &&
PSC_REG_MDSTAT_GET_LRSTDONE(mdstat))
break;
timeout--;
}
if (!timeout) {
printf("%s: Timedout waiting for mdstat(0x%08x) to change\n",
__func__, mdstat);
return -ETIMEDOUT;
}
return 0;
}