|  | /* | 
|  | * 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 { | 
|  | 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; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * 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 | 
|  | */ | 
|  | 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); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * 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 */ | 
|  | 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); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * 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 */ | 
|  | 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); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * 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 */ | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * FUNCTION PURPOSE: Disable a power domain | 
|  | * | 
|  | * DESCRIPTION: The power domain is disabled | 
|  | */ | 
|  | 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); | 
|  | } |