Commit e745f992 authored by Peter De Schrijver's avatar Peter De Schrijver Committed by Thierry Reding

clk: tegra: Rework pll_u

In normal operation pll_u is under hardware control and has a fixed rate
of 480MHz. Hardware will turn on pll_u on whenever any of the XUSB
powerdomains is on. From a software point of view we model this is if
pll_u is always on using a fixed rate clock. However the bootloader
might or might not have configured pll_u this way. So we will check the
current state of pll_u at boot and reconfigure it if required.

There are 3 possiblities at kernel boot:
1) pll_u is under hardware control: do nothing
2) pll_u is under hardware control and enabled: enable hardware control
3) pll_u is disabled: enable pll_u and enable hardware control

In all cases we also check if UTMIPLL is under hardware control at boot
and configure it for hardware control if that is not the case.
The same is done during SC7 resume.

Thanks to Joseph Lo <josephl@nvidia.com> for bug fixes.
Signed-off-by: default avatarPeter De Schrijver <pdeschrijver@nvidia.com>
Signed-off-by: default avatarThierry Reding <treding@nvidia.com>
parent 4236e752
......@@ -2517,152 +2517,6 @@ static int clk_plle_tegra210_is_enabled(struct clk_hw *hw)
return val & PLLE_BASE_ENABLE ? 1 : 0;
}
static int clk_pllu_tegra210_enable(struct clk_hw *hw)
{
struct tegra_clk_pll *pll = to_clk_pll(hw);
struct clk_hw *pll_ref = clk_hw_get_parent(hw);
struct clk_hw *osc = clk_hw_get_parent(pll_ref);
const struct utmi_clk_param *params = NULL;
unsigned long flags = 0, input_rate;
unsigned int i;
int ret = 0;
u32 value;
if (!osc) {
pr_err("%s: failed to get OSC clock\n", __func__);
return -EINVAL;
}
input_rate = clk_hw_get_rate(osc);
if (pll->lock)
spin_lock_irqsave(pll->lock, flags);
_clk_pll_enable(hw);
ret = clk_pll_wait_for_lock(pll);
if (ret < 0)
goto out;
for (i = 0; i < ARRAY_SIZE(utmi_parameters); i++) {
if (input_rate == utmi_parameters[i].osc_frequency) {
params = &utmi_parameters[i];
break;
}
}
if (!params) {
pr_err("%s: unexpected input rate %lu Hz\n", __func__,
input_rate);
ret = -EINVAL;
goto out;
}
value = pll_readl_base(pll);
value &= ~PLLU_BASE_OVERRIDE;
pll_writel_base(value, pll);
/* Put PLLU under HW control */
value = readl_relaxed(pll->clk_base + PLLU_HW_PWRDN_CFG0);
value |= PLLU_HW_PWRDN_CFG0_IDDQ_PD_INCLUDE |
PLLU_HW_PWRDN_CFG0_USE_SWITCH_DETECT |
PLLU_HW_PWRDN_CFG0_USE_LOCKDET;
value &= ~(PLLU_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL |
PLLU_HW_PWRDN_CFG0_CLK_SWITCH_SWCTL);
writel_relaxed(value, pll->clk_base + PLLU_HW_PWRDN_CFG0);
value = readl_relaxed(pll->clk_base + XUSB_PLL_CFG0);
value &= ~XUSB_PLL_CFG0_PLLU_LOCK_DLY;
writel_relaxed(value, pll->clk_base + XUSB_PLL_CFG0);
udelay(1);
value = readl_relaxed(pll->clk_base + PLLU_HW_PWRDN_CFG0);
value |= PLLU_HW_PWRDN_CFG0_SEQ_ENABLE;
writel_relaxed(value, pll->clk_base + PLLU_HW_PWRDN_CFG0);
udelay(1);
/* Disable PLLU clock branch to UTMIPLL since it uses OSC */
value = pll_readl_base(pll);
value &= ~PLLU_BASE_CLKENABLE_USB;
pll_writel_base(value, pll);
value = readl_relaxed(pll->clk_base + UTMIPLL_HW_PWRDN_CFG0);
if (value & UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE) {
pr_debug("UTMIPLL already enabled\n");
goto out;
}
value &= ~UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE;
writel_relaxed(value, pll->clk_base + UTMIPLL_HW_PWRDN_CFG0);
/* Program UTMIP PLL stable and active counts */
value = readl_relaxed(pll->clk_base + UTMIP_PLL_CFG2);
value &= ~UTMIP_PLL_CFG2_STABLE_COUNT(~0);
value |= UTMIP_PLL_CFG2_STABLE_COUNT(params->stable_count);
value &= ~UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(~0);
value |= UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(params->active_delay_count);
value |= UTMIP_PLL_CFG2_PHY_XTAL_CLOCKEN;
writel_relaxed(value, pll->clk_base + UTMIP_PLL_CFG2);
/* Program UTMIP PLL delay and oscillator frequency counts */
value = readl_relaxed(pll->clk_base + UTMIP_PLL_CFG1);
value &= ~UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(~0);
value |= UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(params->enable_delay_count);
value &= ~UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(~0);
value |= UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(params->xtal_freq_count);
writel_relaxed(value, pll->clk_base + UTMIP_PLL_CFG1);
/* Remove power downs from UTMIP PLL control bits */
value = readl_relaxed(pll->clk_base + UTMIP_PLL_CFG1);
value &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN;
value |= UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP;
writel(value, pll->clk_base + UTMIP_PLL_CFG1);
udelay(1);
/* Enable samplers for SNPS, XUSB_HOST, XUSB_DEV */
value = readl_relaxed(pll->clk_base + UTMIP_PLL_CFG2);
value |= UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERUP;
value |= UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERUP;
value |= UTMIP_PLL_CFG2_FORCE_PD_SAMP_D_POWERUP;
value &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN;
value &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN;
value &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_D_POWERDOWN;
writel_relaxed(value, pll->clk_base + UTMIP_PLL_CFG2);
/* Setup HW control of UTMIPLL */
value = readl_relaxed(pll->clk_base + UTMIP_PLL_CFG1);
value &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP;
value &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN;
writel_relaxed(value, pll->clk_base + UTMIP_PLL_CFG1);
value = readl_relaxed(pll->clk_base + UTMIPLL_HW_PWRDN_CFG0);
value |= UTMIPLL_HW_PWRDN_CFG0_USE_LOCKDET;
value &= ~UTMIPLL_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL;
writel_relaxed(value, pll->clk_base + UTMIPLL_HW_PWRDN_CFG0);
udelay(1);
value = readl_relaxed(pll->clk_base + XUSB_PLL_CFG0);
value &= ~XUSB_PLL_CFG0_UTMIPLL_LOCK_DLY;
writel_relaxed(value, pll->clk_base + XUSB_PLL_CFG0);
udelay(1);
/* Enable HW control of UTMIPLL */
value = readl_relaxed(pll->clk_base + UTMIPLL_HW_PWRDN_CFG0);
value |= UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE;
writel_relaxed(value, pll->clk_base + UTMIPLL_HW_PWRDN_CFG0);
out:
if (pll->lock)
spin_unlock_irqrestore(pll->lock, flags);
return ret;
}
static const struct clk_ops tegra_clk_plle_tegra210_ops = {
.is_enabled = clk_plle_tegra210_is_enabled,
.enable = clk_plle_tegra210_enable,
......@@ -2670,13 +2524,6 @@ static const struct clk_ops tegra_clk_plle_tegra210_ops = {
.recalc_rate = clk_pll_recalc_rate,
};
static const struct clk_ops tegra_clk_pllu_tegra210_ops = {
.is_enabled = clk_pll_is_enabled,
.enable = clk_pllu_tegra210_enable,
.disable = clk_pll_disable,
.recalc_rate = clk_pllre_recalc_rate,
};
struct clk *tegra_clk_register_plle_tegra210(const char *name,
const char *parent_name,
void __iomem *clk_base, unsigned long flags,
......@@ -2918,25 +2765,4 @@ struct clk *tegra_clk_register_pllmb(const char *name, const char *parent_name,
return clk;
}
struct clk *tegra_clk_register_pllu_tegra210(const char *name,
const char *parent_name, void __iomem *clk_base,
unsigned long flags, struct tegra_clk_pll_params *pll_params,
spinlock_t *lock)
{
struct tegra_clk_pll *pll;
struct clk *clk;
pll_params->flags |= TEGRA_PLLU;
pll = _tegra_init_pll(clk_base, NULL, pll_params, lock);
if (IS_ERR(pll))
return ERR_CAST(pll);
clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
&tegra_clk_pllu_tegra210_ops);
if (IS_ERR(clk))
kfree(pll);
return clk;
}
#endif
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment