2014-04-04 17:16:53 +00:00
|
|
|
/*
|
|
|
|
* Keystone: PSC configuration module
|
|
|
|
*
|
|
|
|
* (C) Copyright 2012-2014
|
|
|
|
* Texas Instruments Incorporated, <www.ti.com>
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <common.h>
|
|
|
|
#include <asm-generic/errno.h>
|
|
|
|
#include <asm/io.h>
|
|
|
|
#include <asm/processor.h>
|
|
|
|
#include <asm/arch/psc_defs.h>
|
|
|
|
|
|
|
|
int psc_delay(void)
|
|
|
|
{
|
|
|
|
udelay(10);
|
|
|
|
return 10;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FUNCTION PURPOSE: Wait for end of transitional state
|
|
|
|
*
|
|
|
|
* DESCRIPTION: 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.
|
|
|
|
*
|
|
|
|
* Returns 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 {
|
2014-08-28 13:07:45 +00:00
|
|
|
ptstat = __raw_readl(KS2_PSC_BASE + PSC_REG_PSTAT);
|
2014-04-04 17:16:53 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 psc_get_domain_num(u32 mod_num)
|
|
|
|
{
|
|
|
|
u32 domain_num;
|
|
|
|
|
|
|
|
/* Get the power domain associated with the module number */
|
2014-08-28 13:07:45 +00:00
|
|
|
domain_num = __raw_readl(KS2_PSC_BASE + PSC_REG_MDCFG(mod_num));
|
2014-04-04 17:16:53 +00:00
|
|
|
domain_num = PSC_REG_MDCFG_GET_PD(domain_num);
|
|
|
|
|
|
|
|
return domain_num;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FUNCTION PURPOSE: Power up/down a module
|
|
|
|
*
|
|
|
|
* DESCRIPTION: 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.
|
|
|
|
*
|
|
|
|
* Returns 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
|
|
|
|
*/
|
2014-08-28 13:07:45 +00:00
|
|
|
v = __raw_readl(KS2_PSC_BASE + PSC_REG_MDCFG(mod_num));
|
2014-04-04 17:16:53 +00:00
|
|
|
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) {
|
2014-08-28 13:07:45 +00:00
|
|
|
pdctl = __raw_readl(KS2_PSC_BASE + PSC_REG_PDCTL(domain_num));
|
2014-04-04 17:16:53 +00:00
|
|
|
pdctl = PSC_REG_PDCTL_SET_NEXT(pdctl,
|
|
|
|
PSC_REG_VAL_PDCTL_NEXT_ON);
|
2014-08-28 13:07:45 +00:00
|
|
|
__raw_writel(pdctl, KS2_PSC_BASE + PSC_REG_PDCTL(domain_num));
|
2014-04-04 17:16:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the next state for the module to enabled/disabled */
|
2014-08-28 13:07:45 +00:00
|
|
|
mdctl = __raw_readl(KS2_PSC_BASE + PSC_REG_MDCTL(mod_num));
|
2014-04-04 17:16:53 +00:00
|
|
|
mdctl = PSC_REG_MDCTL_SET_NEXT(mdctl, state);
|
|
|
|
mdctl = PSC_REG_MDCTL_SET_RESET_ISO(mdctl, reset_iso);
|
2014-08-28 13:07:45 +00:00
|
|
|
__raw_writel(mdctl, KS2_PSC_BASE + PSC_REG_MDCTL(mod_num));
|
2014-04-04 17:16:53 +00:00
|
|
|
|
|
|
|
/* Trigger the enable */
|
2014-08-28 13:07:45 +00:00
|
|
|
ptcmd = __raw_readl(KS2_PSC_BASE + PSC_REG_PTCMD);
|
2014-04-04 17:16:53 +00:00
|
|
|
ptcmd |= (u32)(1<<domain_num);
|
2014-08-28 13:07:45 +00:00
|
|
|
__raw_writel(ptcmd, KS2_PSC_BASE + PSC_REG_PTCMD);
|
2014-04-04 17:16:53 +00:00
|
|
|
|
|
|
|
/* Wait on the complete */
|
|
|
|
return psc_wait(domain_num);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FUNCTION PURPOSE: Power up a module
|
|
|
|
*
|
|
|
|
* DESCRIPTION: Powers up the requested module and the associated power domain
|
|
|
|
* if required. No action is taken it the module is already
|
|
|
|
* powered up.
|
|
|
|
*
|
|
|
|
* Returns 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 */
|
2014-08-28 13:07:45 +00:00
|
|
|
mdctl = __raw_readl(KS2_PSC_BASE + PSC_REG_MDCTL(mod_num));
|
2014-04-04 17:16:53 +00:00
|
|
|
if ((mdctl & 0x3f) == PSC_REG_VAL_MDSTAT_STATE_ON)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return psc_set_state(mod_num, PSC_REG_VAL_MDCTL_NEXT_ON);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FUNCTION PURPOSE: Power down a module
|
|
|
|
*
|
|
|
|
* DESCRIPTION: Powers down the requested module.
|
|
|
|
*
|
|
|
|
* Returns 0 on success, -1 on failure or timeout.
|
|
|
|
*/
|
|
|
|
int psc_disable_module(u32 mod_num)
|
|
|
|
{
|
|
|
|
u32 mdctl;
|
|
|
|
|
|
|
|
/* Set the bit to apply reset */
|
2014-08-28 13:07:45 +00:00
|
|
|
mdctl = __raw_readl(KS2_PSC_BASE + PSC_REG_MDCTL(mod_num));
|
2014-04-04 17:16:53 +00:00
|
|
|
if ((mdctl & 0x3f) == 0)
|
|
|
|
return 0;
|
|
|
|
mdctl = PSC_REG_MDCTL_SET_LRSTZ(mdctl, 0);
|
2014-08-28 13:07:45 +00:00
|
|
|
__raw_writel(mdctl, KS2_PSC_BASE + PSC_REG_MDCTL(mod_num));
|
2014-04-04 17:16:53 +00:00
|
|
|
|
|
|
|
return psc_set_state(mod_num, PSC_REG_VAL_MDCTL_NEXT_SWRSTDISABLE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FUNCTION PURPOSE: Set the reset isolation bit in mdctl
|
|
|
|
*
|
|
|
|
* DESCRIPTION: The reset isolation enable bit is set. The state of the module
|
|
|
|
* is not changed. Returns 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 */
|
2014-08-28 13:07:45 +00:00
|
|
|
mdctl = __raw_readl(KS2_PSC_BASE + PSC_REG_MDCTL(mod_num));
|
2014-04-04 17:16:53 +00:00
|
|
|
mdctl = PSC_REG_MDCTL_SET_RESET_ISO(mdctl, 1);
|
2014-08-28 13:07:45 +00:00
|
|
|
__raw_writel(mdctl, KS2_PSC_BASE + PSC_REG_MDCTL(mod_num));
|
2014-04-04 17:16:53 +00:00
|
|
|
|
2014-08-28 13:07:45 +00:00
|
|
|
v = __raw_readl(KS2_PSC_BASE + PSC_REG_MDCFG(mod_num));
|
2014-04-04 17:16:53 +00:00
|
|
|
if (PSC_REG_MDCFG_GET_RESET_ISO(v) == 1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FUNCTION PURPOSE: Disable a power domain
|
|
|
|
*
|
|
|
|
* DESCRIPTION: The power domain is disabled
|
|
|
|
*/
|
|
|
|
int psc_disable_domain(u32 domain_num)
|
|
|
|
{
|
|
|
|
u32 pdctl;
|
|
|
|
u32 ptcmd;
|
|
|
|
|
2014-08-28 13:07:45 +00:00
|
|
|
pdctl = __raw_readl(KS2_PSC_BASE + PSC_REG_PDCTL(domain_num));
|
2014-04-04 17:16:53 +00:00
|
|
|
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);
|
2014-08-28 13:07:45 +00:00
|
|
|
__raw_writel(pdctl, KS2_PSC_BASE + PSC_REG_PDCTL(domain_num));
|
2014-04-04 17:16:53 +00:00
|
|
|
|
2014-08-28 13:07:45 +00:00
|
|
|
ptcmd = __raw_readl(KS2_PSC_BASE + PSC_REG_PTCMD);
|
2014-04-04 17:16:53 +00:00
|
|
|
ptcmd |= (u32)(1 << domain_num);
|
2014-08-28 13:07:45 +00:00
|
|
|
__raw_writel(ptcmd, KS2_PSC_BASE + PSC_REG_PTCMD);
|
2014-04-04 17:16:53 +00:00
|
|
|
|
|
|
|
return psc_wait(domain_num);
|
|
|
|
}
|