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