diff options
Diffstat (limited to 'drivers/clk/tegra/clk-tegra210.c')
-rw-r--r-- | drivers/clk/tegra/clk-tegra210.c | 295 |
1 files changed, 272 insertions, 23 deletions
diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c index f89d2a912273..1a8121abd427 100644 --- a/drivers/clk/tegra/clk-tegra210.c +++ b/drivers/clk/tegra/clk-tegra210.c @@ -24,6 +24,7 @@ #include <linux/export.h> #include <linux/clk/tegra.h> #include <dt-bindings/clock/tegra210-car.h> +#include <linux/iopoll.h> #include "clk.h" #include "clk-id.h" @@ -155,6 +156,27 @@ #define PMC_PLLM_WB0_OVERRIDE 0x1dc #define PMC_PLLM_WB0_OVERRIDE_2 0x2b0 +#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_CFG1 0x484 +#define UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 27) +#define UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0) +#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERUP BIT(17) +#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN BIT(16) +#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP BIT(15) +#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN BIT(14) +#define UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN BIT(12) + #define SATA_PLL_CFG0 0x490 #define SATA_PLL_CFG0_PADPLL_RESET_SWCTL BIT(0) #define SATA_PLL_CFG0_PADPLL_USE_LOCKDET BIT(2) @@ -1051,27 +1073,28 @@ static void tegra210_pllp_set_defaults(struct tegra_clk_pll *pllp) * Both VCO and post-divider output rates are fixed at 480MHz and 240MHz, * respectively. */ -static void pllu_check_defaults(struct tegra_clk_pll *pll, bool hw_control) +static void pllu_check_defaults(struct tegra_clk_pll_params *params, + bool hw_control) { u32 val, mask; /* Ignore lock enable (will be set) and IDDQ if under h/w control */ val = PLLU_MISC0_DEFAULT_VALUE & (~PLLU_MISC0_IDDQ); mask = PLLU_MISC0_LOCK_ENABLE | (hw_control ? PLLU_MISC0_IDDQ : 0); - _pll_misc_chk_default(clk_base, pll->params, 0, val, + _pll_misc_chk_default(clk_base, params, 0, val, ~mask & PLLU_MISC0_WRITE_MASK); val = PLLU_MISC1_DEFAULT_VALUE; mask = PLLU_MISC1_LOCK_OVERRIDE; - _pll_misc_chk_default(clk_base, pll->params, 1, val, + _pll_misc_chk_default(clk_base, params, 1, val, ~mask & PLLU_MISC1_WRITE_MASK); } -static void tegra210_pllu_set_defaults(struct tegra_clk_pll *pllu) +static void tegra210_pllu_set_defaults(struct tegra_clk_pll_params *pllu) { - u32 val = readl_relaxed(clk_base + pllu->params->base_reg); + u32 val = readl_relaxed(clk_base + pllu->base_reg); - pllu->params->defaults_set = true; + pllu->defaults_set = true; if (val & PLL_ENABLE) { @@ -1080,19 +1103,19 @@ static void tegra210_pllu_set_defaults(struct tegra_clk_pll *pllu) * that can be updated in flight. */ pllu_check_defaults(pllu, false); - if (!pllu->params->defaults_set) + if (!pllu->defaults_set) pr_warn("PLL_U already enabled. Postponing set full defaults\n"); /* Enable lock detect */ - val = readl_relaxed(clk_base + pllu->params->ext_misc_reg[0]); + val = readl_relaxed(clk_base + pllu->ext_misc_reg[0]); val &= ~PLLU_MISC0_LOCK_ENABLE; val |= PLLU_MISC0_DEFAULT_VALUE & PLLU_MISC0_LOCK_ENABLE; - writel_relaxed(val, clk_base + pllu->params->ext_misc_reg[0]); + writel_relaxed(val, clk_base + pllu->ext_misc_reg[0]); - val = readl_relaxed(clk_base + pllu->params->ext_misc_reg[1]); + val = readl_relaxed(clk_base + pllu->ext_misc_reg[1]); val &= ~PLLU_MISC1_LOCK_OVERRIDE; val |= PLLU_MISC1_DEFAULT_VALUE & PLLU_MISC1_LOCK_OVERRIDE; - writel_relaxed(val, clk_base + pllu->params->ext_misc_reg[1]); + writel_relaxed(val, clk_base + pllu->ext_misc_reg[1]); udelay(1); return; @@ -1100,9 +1123,9 @@ static void tegra210_pllu_set_defaults(struct tegra_clk_pll *pllu) /* set IDDQ, enable lock detect */ writel_relaxed(PLLU_MISC0_DEFAULT_VALUE, - clk_base + pllu->params->ext_misc_reg[0]); + clk_base + pllu->ext_misc_reg[0]); writel_relaxed(PLLU_MISC1_DEFAULT_VALUE, - clk_base + pllu->params->ext_misc_reg[1]); + clk_base + pllu->ext_misc_reg[1]); udelay(1); } @@ -1999,9 +2022,9 @@ static struct div_nmp pllu_nmp = { }; static struct tegra_clk_pll_freq_table pll_u_freq_table[] = { - { 12000000, 480000000, 40, 1, 1, 0 }, - { 13000000, 480000000, 36, 1, 1, 0 }, /* actual: 468.0 MHz */ - { 38400000, 480000000, 25, 2, 1, 0 }, + { 12000000, 480000000, 40, 1, 0, 0 }, + { 13000000, 480000000, 36, 1, 0, 0 }, /* actual: 468.0 MHz */ + { 38400000, 480000000, 25, 2, 0, 0 }, { 0, 0, 0, 0, 0, 0 }, }; @@ -2025,8 +2048,47 @@ static struct tegra_clk_pll_params pll_u_vco_params = { .div_nmp = &pllu_nmp, .freq_table = pll_u_freq_table, .flags = TEGRA_PLLU | TEGRA_PLL_USE_LOCK | TEGRA_PLL_VCO_OUT, - .set_defaults = tegra210_pllu_set_defaults, - .calc_rate = tegra210_pll_fixed_mdiv_cfg, +}; + +struct utmi_clk_param { + /* Oscillator Frequency in KHz */ + u32 osc_frequency; + /* UTMIP PLL Enable Delay Count */ + u8 enable_delay_count; + /* UTMIP PLL Stable count */ + u16 stable_count; + /* UTMIP PLL Active delay count */ + u8 active_delay_count; + /* UTMIP PLL Xtal frequency count */ + u16 xtal_freq_count; +}; + +static const struct utmi_clk_param utmi_parameters[] = { + { + .osc_frequency = 38400000, .enable_delay_count = 0x0, + .stable_count = 0x0, .active_delay_count = 0x6, + .xtal_freq_count = 0x80 + }, { + .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 = 0x08, + .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 + }, }; static struct tegra_clk tegra210_clks[tegra_clk_max] __initdata = { @@ -2339,6 +2401,190 @@ void tegra210_put_utmipll_out_iddq(void) } EXPORT_SYMBOL_GPL(tegra210_put_utmipll_out_iddq); +static void tegra210_utmi_param_configure(void) +{ + u32 reg; + int i; + + for (i = 0; i < ARRAY_SIZE(utmi_parameters); i++) { + if (osc_freq == utmi_parameters[i].osc_frequency) + break; + } + + if (i >= ARRAY_SIZE(utmi_parameters)) { + pr_err("%s: Unexpected oscillator freq %lu\n", __func__, + osc_freq); + return; + } + + reg = readl_relaxed(clk_base + UTMIPLL_HW_PWRDN_CFG0); + reg &= ~UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE; + writel_relaxed(reg, clk_base + UTMIPLL_HW_PWRDN_CFG0); + + udelay(10); + + reg = readl_relaxed(clk_base + UTMIP_PLL_CFG2); + + /* Program UTMIP PLL stable and active counts */ + /* [FIXME] arclk_rst.h says WRONG! This should be 1ms -> 0x50 Check! */ + reg &= ~UTMIP_PLL_CFG2_STABLE_COUNT(~0); + reg |= UTMIP_PLL_CFG2_STABLE_COUNT(utmi_parameters[i].stable_count); + + reg &= ~UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(~0); + + reg |= + UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(utmi_parameters[i].active_delay_count); + writel_relaxed(reg, clk_base + UTMIP_PLL_CFG2); + + /* Program UTMIP PLL delay and oscillator frequency counts */ + reg = readl_relaxed(clk_base + UTMIP_PLL_CFG1); + reg &= ~UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(~0); + + reg |= + UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(utmi_parameters[i].enable_delay_count); + + reg &= ~UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(~0); + reg |= + UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(utmi_parameters[i].xtal_freq_count); + + reg |= UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN; + writel_relaxed(reg, clk_base + UTMIP_PLL_CFG1); + + /* Remove power downs from UTMIP PLL control bits */ + reg = readl_relaxed(clk_base + UTMIP_PLL_CFG1); + reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN; + reg |= UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP; + writel_relaxed(reg, clk_base + UTMIP_PLL_CFG1); + udelay(1); + + /* Enable samplers for SNPS, XUSB_HOST, XUSB_DEV */ + reg = readl_relaxed(clk_base + UTMIP_PLL_CFG2); + reg |= UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERUP; + reg |= UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERUP; + reg |= UTMIP_PLL_CFG2_FORCE_PD_SAMP_D_POWERUP; + reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN; + reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN; + reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_D_POWERDOWN; + writel_relaxed(reg, clk_base + UTMIP_PLL_CFG2); + + /* Setup HW control of UTMIPLL */ + reg = readl_relaxed(clk_base + UTMIP_PLL_CFG1); + reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN; + reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP; + writel_relaxed(reg, clk_base + UTMIP_PLL_CFG1); + + reg = readl_relaxed(clk_base + UTMIPLL_HW_PWRDN_CFG0); + reg |= UTMIPLL_HW_PWRDN_CFG0_USE_LOCKDET; + reg &= ~UTMIPLL_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL; + writel_relaxed(reg, clk_base + UTMIPLL_HW_PWRDN_CFG0); + + udelay(1); + + reg = readl_relaxed(clk_base + XUSB_PLL_CFG0); + reg &= ~XUSB_PLL_CFG0_UTMIPLL_LOCK_DLY; + writel_relaxed(reg, clk_base + XUSB_PLL_CFG0); + + udelay(1); + + /* Enable HW control UTMIPLL */ + reg = readl_relaxed(clk_base + UTMIPLL_HW_PWRDN_CFG0); + reg |= UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE; + writel_relaxed(reg, clk_base + UTMIPLL_HW_PWRDN_CFG0); +} + +static int tegra210_enable_pllu(void) +{ + struct tegra_clk_pll_freq_table *fentry; + struct tegra_clk_pll pllu; + u32 reg; + + for (fentry = pll_u_freq_table; fentry->input_rate; fentry++) { + if (fentry->input_rate == pll_ref_freq) + break; + } + + if (!fentry->input_rate) { + pr_err("Unknown PLL_U reference frequency %lu\n", pll_ref_freq); + return -EINVAL; + } + + /* clear IDDQ bit */ + pllu.params = &pll_u_vco_params; + reg = readl_relaxed(clk_base + pllu.params->ext_misc_reg[0]); + reg &= ~BIT(pllu.params->iddq_bit_idx); + writel_relaxed(reg, clk_base + pllu.params->ext_misc_reg[0]); + + reg = readl_relaxed(clk_base + PLLU_BASE); + reg &= ~GENMASK(20, 0); + reg |= fentry->m; + reg |= fentry->n << 8; + reg |= fentry->p << 16; + writel(reg, clk_base + PLLU_BASE); + reg |= PLL_ENABLE; + writel(reg, clk_base + PLLU_BASE); + + readl_relaxed_poll_timeout(clk_base + PLLU_BASE, reg, + reg & PLL_BASE_LOCK, 2, 1000); + if (!(reg & PLL_BASE_LOCK)) { + pr_err("Timed out waiting for PLL_U to lock\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int tegra210_init_pllu(void) +{ + u32 reg; + int err; + + tegra210_pllu_set_defaults(&pll_u_vco_params); + /* skip initialization when pllu is in hw controlled mode */ + reg = readl_relaxed(clk_base + PLLU_BASE); + if (reg & PLLU_BASE_OVERRIDE) { + if (!(reg & PLL_ENABLE)) { + err = tegra210_enable_pllu(); + if (err < 0) { + WARN_ON(1); + return err; + } + } + /* enable hw controlled mode */ + reg = readl_relaxed(clk_base + PLLU_BASE); + reg &= ~PLLU_BASE_OVERRIDE; + writel(reg, clk_base + PLLU_BASE); + + reg = readl_relaxed(clk_base + PLLU_HW_PWRDN_CFG0); + reg |= PLLU_HW_PWRDN_CFG0_IDDQ_PD_INCLUDE | + PLLU_HW_PWRDN_CFG0_USE_SWITCH_DETECT | + PLLU_HW_PWRDN_CFG0_USE_LOCKDET; + reg &= ~(PLLU_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL | + PLLU_HW_PWRDN_CFG0_CLK_SWITCH_SWCTL); + writel_relaxed(reg, clk_base + PLLU_HW_PWRDN_CFG0); + + reg = readl_relaxed(clk_base + XUSB_PLL_CFG0); + reg &= ~XUSB_PLL_CFG0_PLLU_LOCK_DLY_MASK; + writel_relaxed(reg, clk_base + XUSB_PLL_CFG0); + udelay(1); + + reg = readl_relaxed(clk_base + PLLU_HW_PWRDN_CFG0); + reg |= PLLU_HW_PWRDN_CFG0_SEQ_ENABLE; + writel_relaxed(reg, clk_base + PLLU_HW_PWRDN_CFG0); + udelay(1); + + reg = readl_relaxed(clk_base + PLLU_BASE); + reg &= ~PLLU_BASE_CLKENABLE_USB; + writel_relaxed(reg, clk_base + PLLU_BASE); + } + + /* enable UTMIPLL hw control if not yet done by the bootloader */ + reg = readl_relaxed(clk_base + UTMIPLL_HW_PWRDN_CFG0); + if (!(reg & UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE)) + tegra210_utmi_param_configure(); + + return 0; +} + static __init void tegra210_periph_clk_init(void __iomem *clk_base, void __iomem *pmc_base) { @@ -2467,11 +2713,12 @@ static void __init tegra210_pll_init(void __iomem *clk_base, clks[TEGRA210_CLK_PLL_M_UD] = clk; /* PLLU_VCO */ - clk = tegra_clk_register_pllu_tegra210("pll_u_vco", "pll_ref", - clk_base, 0, &pll_u_vco_params, - &pll_u_lock); - clk_register_clkdev(clk, "pll_u_vco", NULL); - clks[TEGRA210_CLK_PLL_U] = clk; + if (!tegra210_init_pllu()) { + clk = clk_register_fixed_rate(NULL, "pll_u_vco", "pll_ref", 0, + 480*1000*1000); + clk_register_clkdev(clk, "pll_u_vco", NULL); + clks[TEGRA210_CLK_PLL_U] = clk; + } /* PLLU_OUT */ clk = clk_register_divider_table(NULL, "pll_u_out", "pll_u_vco", 0, @@ -2716,6 +2963,8 @@ static struct tegra_clk_init_table init_table[] __initdata = { { TEGRA210_CLK_PLL_DP, TEGRA210_CLK_CLK_MAX, 270000000, 0 }, { TEGRA210_CLK_SOC_THERM, TEGRA210_CLK_PLL_P, 51000000, 0 }, { TEGRA210_CLK_CCLK_G, TEGRA210_CLK_CLK_MAX, 0, 1 }, + { TEGRA210_CLK_PLL_U_OUT1, TEGRA210_CLK_CLK_MAX, 48000000, 1 }, + { TEGRA210_CLK_PLL_U_OUT2, TEGRA210_CLK_CLK_MAX, 60000000, 1 }, /* This MUST be the last entry. */ { TEGRA210_CLK_CLK_MAX, TEGRA210_CLK_CLK_MAX, 0, 0 }, }; |