|  | /* | 
|  | * Keystone: PSC configuration module | 
|  | * | 
|  | * (C) Copyright 2012-2014 | 
|  | *     Texas Instruments Incorporated, <www.ti.com> | 
|  | * | 
|  | * SPDX-License-Identifier:     GPL-2.0+ | 
|  | */ | 
|  |  | 
|  | #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; | 
|  | } |