|
@@ -191,6 +191,53 @@
|
|
|
#define PLLSS_REF_SRC_SEL_SHIFT 25
|
|
|
#define PLLSS_REF_SRC_SEL_MASK (3 << PLLSS_REF_SRC_SEL_SHIFT)
|
|
|
|
|
|
+#define UTMIP_PLL_CFG1 0x484
|
|
|
+#define UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0)
|
|
|
+#define UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 27)
|
|
|
+#define UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN BIT(12)
|
|
|
+#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN BIT(14)
|
|
|
+#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP BIT(15)
|
|
|
+#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN BIT(16)
|
|
|
+#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERUP BIT(17)
|
|
|
+
|
|
|
+#define UTMIP_PLL_CFG2 0x488
|
|
|
+#define UTMIP_PLL_CFG2_STABLE_COUNT(x) (((x) & 0xfff) << 6)
|
|
|
+#define UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(x) (((x) & 0x3f) << 18)
|
|
|
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN BIT(0)
|
|
|
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERUP BIT(1)
|
|
|
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN BIT(2)
|
|
|
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERUP BIT(3)
|
|
|
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN BIT(4)
|
|
|
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERUP BIT(5)
|
|
|
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_D_POWERDOWN BIT(24)
|
|
|
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_D_POWERUP BIT(25)
|
|
|
+#define UTMIP_PLL_CFG2_PHY_XTAL_CLOCKEN BIT(30)
|
|
|
+
|
|
|
+#define UTMIPLL_HW_PWRDN_CFG0 0x52c
|
|
|
+#define UTMIPLL_HW_PWRDN_CFG0_IDDQ_SWCTL BIT(0)
|
|
|
+#define UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE BIT(1)
|
|
|
+#define UTMIPLL_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL BIT(2)
|
|
|
+#define UTMIPLL_HW_PWRDN_CFG0_SEQ_IN_SWCTL BIT(4)
|
|
|
+#define UTMIPLL_HW_PWRDN_CFG0_SEQ_RESET_INPUT_VALUE BIT(5)
|
|
|
+#define UTMIPLL_HW_PWRDN_CFG0_USE_LOCKDET BIT(6)
|
|
|
+#define UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE BIT(24)
|
|
|
+#define UTMIPLL_HW_PWRDN_CFG0_SEQ_START_STATE BIT(25)
|
|
|
+
|
|
|
+#define PLLU_HW_PWRDN_CFG0 0x530
|
|
|
+#define PLLU_HW_PWRDN_CFG0_CLK_SWITCH_SWCTL BIT(0)
|
|
|
+#define PLLU_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL BIT(2)
|
|
|
+#define PLLU_HW_PWRDN_CFG0_USE_LOCKDET BIT(6)
|
|
|
+#define PLLU_HW_PWRDN_CFG0_USE_SWITCH_DETECT BIT(7)
|
|
|
+#define PLLU_HW_PWRDN_CFG0_SEQ_ENABLE BIT(24)
|
|
|
+#define PLLU_HW_PWRDN_CFG0_IDDQ_PD_INCLUDE BIT(28)
|
|
|
+
|
|
|
+#define XUSB_PLL_CFG0 0x534
|
|
|
+#define XUSB_PLL_CFG0_UTMIPLL_LOCK_DLY 0x3ff
|
|
|
+#define XUSB_PLL_CFG0_PLLU_LOCK_DLY (0x3ff << 14)
|
|
|
+
|
|
|
+#define PLLU_BASE_CLKENABLE_USB BIT(21)
|
|
|
+#define PLLU_BASE_OVERRIDE BIT(24)
|
|
|
+
|
|
|
#define pll_readl(offset, p) readl_relaxed(p->clk_base + offset)
|
|
|
#define pll_readl_base(p) pll_readl(p->params->base_reg, p)
|
|
|
#define pll_readl_misc(p) pll_readl(p->params->misc_reg, p)
|
|
@@ -973,6 +1020,133 @@ const struct clk_ops tegra_clk_plle_ops = {
|
|
|
.enable = clk_plle_enable,
|
|
|
};
|
|
|
|
|
|
+/*
|
|
|
+ * Structure defining the fields for USB UTMI clocks Parameters.
|
|
|
+ */
|
|
|
+struct utmi_clk_param {
|
|
|
+ /* Oscillator Frequency in Hz */
|
|
|
+ u32 osc_frequency;
|
|
|
+ /* UTMIP PLL Enable Delay Count */
|
|
|
+ u8 enable_delay_count;
|
|
|
+ /* UTMIP PLL Stable count */
|
|
|
+ u8 stable_count;
|
|
|
+ /* UTMIP PLL Active delay count */
|
|
|
+ u8 active_delay_count;
|
|
|
+ /* UTMIP PLL Xtal frequency count */
|
|
|
+ u8 xtal_freq_count;
|
|
|
+};
|
|
|
+
|
|
|
+static const struct utmi_clk_param utmi_parameters[] = {
|
|
|
+ {
|
|
|
+ .osc_frequency = 13000000, .enable_delay_count = 0x02,
|
|
|
+ .stable_count = 0x33, .active_delay_count = 0x05,
|
|
|
+ .xtal_freq_count = 0x7f
|
|
|
+ }, {
|
|
|
+ .osc_frequency = 19200000, .enable_delay_count = 0x03,
|
|
|
+ .stable_count = 0x4b, .active_delay_count = 0x06,
|
|
|
+ .xtal_freq_count = 0xbb
|
|
|
+ }, {
|
|
|
+ .osc_frequency = 12000000, .enable_delay_count = 0x02,
|
|
|
+ .stable_count = 0x2f, .active_delay_count = 0x04,
|
|
|
+ .xtal_freq_count = 0x76
|
|
|
+ }, {
|
|
|
+ .osc_frequency = 26000000, .enable_delay_count = 0x04,
|
|
|
+ .stable_count = 0x66, .active_delay_count = 0x09,
|
|
|
+ .xtal_freq_count = 0xfe
|
|
|
+ }, {
|
|
|
+ .osc_frequency = 16800000, .enable_delay_count = 0x03,
|
|
|
+ .stable_count = 0x41, .active_delay_count = 0x0a,
|
|
|
+ .xtal_freq_count = 0xa4
|
|
|
+ }, {
|
|
|
+ .osc_frequency = 38400000, .enable_delay_count = 0x0,
|
|
|
+ .stable_count = 0x0, .active_delay_count = 0x6,
|
|
|
+ .xtal_freq_count = 0x80
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+static int clk_pllu_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);
|
|
|
+
|
|
|
+ value = readl_relaxed(pll->clk_base + UTMIP_PLL_CFG2);
|
|
|
+ /* Program UTMIP PLL stable and active counts */
|
|
|
+ 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);
|
|
|
+ /* Remove power downs from UTMIP PLL control bits */
|
|
|
+ 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_C_POWERDOWN;
|
|
|
+ writel_relaxed(value, pll->clk_base + UTMIP_PLL_CFG2);
|
|
|
+
|
|
|
+ value = readl_relaxed(pll->clk_base + UTMIP_PLL_CFG1);
|
|
|
+ /* Program UTMIP PLL delay and oscillator frequency counts */
|
|
|
+ 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);
|
|
|
+ /* Remove power downs from UTMIP PLL control bits */
|
|
|
+ value &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN;
|
|
|
+ value &= ~UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN;
|
|
|
+ value &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN;
|
|
|
+ writel_relaxed(value, pll->clk_base + UTMIP_PLL_CFG1);
|
|
|
+
|
|
|
+out:
|
|
|
+ if (pll->lock)
|
|
|
+ spin_unlock_irqrestore(pll->lock, flags);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static const struct clk_ops tegra_clk_pllu_ops = {
|
|
|
+ .is_enabled = clk_pll_is_enabled,
|
|
|
+ .enable = clk_pllu_enable,
|
|
|
+ .disable = clk_pll_disable,
|
|
|
+ .recalc_rate = clk_pll_recalc_rate,
|
|
|
+};
|
|
|
+
|
|
|
static int _pll_fixed_mdiv(struct tegra_clk_pll_params *pll_params,
|
|
|
unsigned long parent_rate)
|
|
|
{
|
|
@@ -1505,6 +1679,112 @@ static void clk_plle_tegra114_disable(struct clk_hw *hw)
|
|
|
if (pll->lock)
|
|
|
spin_unlock_irqrestore(pll->lock, flags);
|
|
|
}
|
|
|
+
|
|
|
+static int clk_pllu_tegra114_enable(struct clk_hw *hw)
|
|
|
+{
|
|
|
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
|
|
|
+ const struct utmi_clk_param *params = NULL;
|
|
|
+ struct clk *osc = __clk_lookup("osc");
|
|
|
+ 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(__clk_get_hw(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);
|
|
|
+
|
|
|
+ value = readl_relaxed(pll->clk_base + UTMIP_PLL_CFG2);
|
|
|
+ /* Program UTMIP PLL stable and active counts */
|
|
|
+ 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);
|
|
|
+ /* Remove power downs from UTMIP PLL control bits */
|
|
|
+ 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_C_POWERDOWN;
|
|
|
+ writel_relaxed(value, pll->clk_base + UTMIP_PLL_CFG2);
|
|
|
+
|
|
|
+ value = readl_relaxed(pll->clk_base + UTMIP_PLL_CFG1);
|
|
|
+ /* Program UTMIP PLL delay and oscillator frequency counts */
|
|
|
+ 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);
|
|
|
+ /* Remove power downs from UTMIP PLL control bits */
|
|
|
+ value &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN;
|
|
|
+ value &= ~UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN;
|
|
|
+ value &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERUP;
|
|
|
+ value &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN;
|
|
|
+ writel_relaxed(value, pll->clk_base + UTMIP_PLL_CFG1);
|
|
|
+
|
|
|
+ /* Setup HW control of UTMIPLL */
|
|
|
+ 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;
|
|
|
+ value |= UTMIPLL_HW_PWRDN_CFG0_SEQ_START_STATE;
|
|
|
+ writel_relaxed(value, pll->clk_base + UTMIPLL_HW_PWRDN_CFG0);
|
|
|
+
|
|
|
+ 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);
|
|
|
+
|
|
|
+ udelay(1);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Setup SW override of UTMIPLL assuming USB2.0 ports are assigned
|
|
|
+ * to USB2
|
|
|
+ */
|
|
|
+ value = readl_relaxed(pll->clk_base + UTMIPLL_HW_PWRDN_CFG0);
|
|
|
+ value |= UTMIPLL_HW_PWRDN_CFG0_IDDQ_SWCTL;
|
|
|
+ value &= ~UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE;
|
|
|
+ writel_relaxed(value, pll->clk_base + UTMIPLL_HW_PWRDN_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;
|
|
|
+}
|
|
|
#endif
|
|
|
|
|
|
static struct tegra_clk_pll *_tegra_init_pll(void __iomem *clk_base,
|
|
@@ -1614,6 +1894,27 @@ struct clk *tegra_clk_register_plle(const char *name, const char *parent_name,
|
|
|
return clk;
|
|
|
}
|
|
|
|
|
|
+struct clk *tegra_clk_register_pllu(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_ops);
|
|
|
+ if (IS_ERR(clk))
|
|
|
+ kfree(pll);
|
|
|
+
|
|
|
+ return clk;
|
|
|
+}
|
|
|
+
|
|
|
#if defined(CONFIG_ARCH_TEGRA_114_SOC) || \
|
|
|
defined(CONFIG_ARCH_TEGRA_124_SOC) || \
|
|
|
defined(CONFIG_ARCH_TEGRA_132_SOC) || \
|
|
@@ -1652,6 +1953,12 @@ static const struct clk_ops tegra_clk_plle_tegra114_ops = {
|
|
|
.recalc_rate = clk_pll_recalc_rate,
|
|
|
};
|
|
|
|
|
|
+static const struct clk_ops tegra_clk_pllu_tegra114_ops = {
|
|
|
+ .is_enabled = clk_pll_is_enabled,
|
|
|
+ .enable = clk_pllu_tegra114_enable,
|
|
|
+ .disable = clk_pll_disable,
|
|
|
+ .recalc_rate = clk_pll_recalc_rate,
|
|
|
+};
|
|
|
|
|
|
struct clk *tegra_clk_register_pllxc(const char *name, const char *parent_name,
|
|
|
void __iomem *clk_base, void __iomem *pmc,
|
|
@@ -1919,6 +2226,29 @@ struct clk *tegra_clk_register_plle_tegra114(const char *name,
|
|
|
|
|
|
return clk;
|
|
|
}
|
|
|
+
|
|
|
+struct clk *
|
|
|
+tegra_clk_register_pllu_tegra114(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_tegra114_ops);
|
|
|
+ if (IS_ERR(clk))
|
|
|
+ kfree(pll);
|
|
|
+
|
|
|
+ return clk;
|
|
|
+}
|
|
|
#endif
|
|
|
|
|
|
#if defined(CONFIG_ARCH_TEGRA_124_SOC) || defined(CONFIG_ARCH_TEGRA_132_SOC)
|
|
@@ -2187,6 +2517,152 @@ 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,
|
|
@@ -2194,6 +2670,13 @@ 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,
|
|
@@ -2434,4 +2917,26 @@ 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
|