diff options
author | Michael Turquette <mturquette@baylibre.com> | 2016-01-04 16:16:45 -0800 |
---|---|---|
committer | Michael Turquette <mturquette@baylibre.com> | 2016-01-04 16:16:45 -0800 |
commit | b360ada3f1534a37b264274cad55932d2a214ec3 (patch) | |
tree | 052ded4d0172924ee55d3d4e2c3ea18146e3b629 /drivers/clk | |
parent | 7ed88aa2efa5422f9d93fd99f2a01c56e28a7409 (diff) | |
parent | 2d7f61f37731f635af47615a8a331ffe7f884934 (diff) |
Merge tag 'tegra-for-4.5-clk' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux into clk-next
clk: tegra: Changes for v4.5-rc1
This set of changes adds support for the Tegra210 SoC and contains a
couple fixes and cleanups.
Diffstat (limited to 'drivers/clk')
-rw-r--r-- | drivers/clk/tegra/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/tegra/clk-id.h | 75 | ||||
-rw-r--r-- | drivers/clk/tegra/clk-pll.c | 834 | ||||
-rw-r--r-- | drivers/clk/tegra/clk-tegra-periph.c | 371 | ||||
-rw-r--r-- | drivers/clk/tegra/clk-tegra-super-gen4.c | 142 | ||||
-rw-r--r-- | drivers/clk/tegra/clk-tegra114.c | 339 | ||||
-rw-r--r-- | drivers/clk/tegra/clk-tegra124.c | 453 | ||||
-rw-r--r-- | drivers/clk/tegra/clk-tegra20.c | 314 | ||||
-rw-r--r-- | drivers/clk/tegra/clk-tegra210.c | 2852 | ||||
-rw-r--r-- | drivers/clk/tegra/clk-tegra30.c | 426 | ||||
-rw-r--r-- | drivers/clk/tegra/clk.h | 101 |
11 files changed, 4980 insertions, 928 deletions
diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile index 826c325dc2e8..97984c503bbb 100644 --- a/drivers/clk/tegra/Makefile +++ b/drivers/clk/tegra/Makefile @@ -20,3 +20,4 @@ obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124.o obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124-dfll-fcpu.o obj-$(CONFIG_ARCH_TEGRA_132_SOC) += clk-tegra124.o obj-y += cvb.o +obj-$(CONFIG_ARCH_TEGRA_210_SOC) += clk-tegra210.o diff --git a/drivers/clk/tegra/clk-id.h b/drivers/clk/tegra/clk-id.h index 60738cc954cb..19ce0738ee76 100644 --- a/drivers/clk/tegra/clk-id.h +++ b/drivers/clk/tegra/clk-id.h @@ -13,6 +13,7 @@ enum clk_id { tegra_clk_amx1, tegra_clk_apbdma, tegra_clk_apbif, + tegra_clk_ape, tegra_clk_audio0, tegra_clk_audio0_2x, tegra_clk_audio0_mux, @@ -38,6 +39,7 @@ enum clk_id { tegra_clk_cile, tegra_clk_clk_32k, tegra_clk_clk72Mhz, + tegra_clk_clk72Mhz_8, tegra_clk_clk_m, tegra_clk_clk_m_div2, tegra_clk_clk_m_div4, @@ -51,17 +53,21 @@ enum clk_id { tegra_clk_cml1, tegra_clk_csi, tegra_clk_csite, + tegra_clk_csite_8, tegra_clk_csus, tegra_clk_cve, tegra_clk_dam0, tegra_clk_dam1, tegra_clk_dam2, tegra_clk_d_audio, + tegra_clk_dbgapb, tegra_clk_dds, tegra_clk_dfll_ref, tegra_clk_dfll_soc, tegra_clk_disp1, + tegra_clk_disp1_8, tegra_clk_disp2, + tegra_clk_disp2_8, tegra_clk_dp2, tegra_clk_dpaux, tegra_clk_dsialp, @@ -71,6 +77,7 @@ enum clk_id { tegra_clk_dtv, tegra_clk_emc, tegra_clk_entropy, + tegra_clk_entropy_8, tegra_clk_epp, tegra_clk_epp_8, tegra_clk_extern1, @@ -85,12 +92,16 @@ enum clk_id { tegra_clk_gr3d_8, tegra_clk_hclk, tegra_clk_hda, + tegra_clk_hda_8, tegra_clk_hda2codec_2x, + tegra_clk_hda2codec_2x_8, tegra_clk_hda2hdmi, tegra_clk_hdmi, tegra_clk_hdmi_audio, tegra_clk_host1x, tegra_clk_host1x_8, + tegra_clk_host1x_9, + tegra_clk_hsic_trk, tegra_clk_i2c1, tegra_clk_i2c2, tegra_clk_i2c3, @@ -110,11 +121,14 @@ enum clk_id { tegra_clk_i2s4_sync, tegra_clk_isp, tegra_clk_isp_8, + tegra_clk_isp_9, tegra_clk_ispb, tegra_clk_kbc, tegra_clk_kfuse, tegra_clk_la, + tegra_clk_maud, tegra_clk_mipi, + tegra_clk_mipibif, tegra_clk_mipi_cal, tegra_clk_mpe, tegra_clk_mselect, @@ -124,15 +138,24 @@ enum clk_id { tegra_clk_ndspeed, tegra_clk_ndspeed_8, tegra_clk_nor, + tegra_clk_nvdec, + tegra_clk_nvenc, + tegra_clk_nvjpg, tegra_clk_owr, + tegra_clk_owr_8, tegra_clk_pcie, tegra_clk_pclk, tegra_clk_pll_a, tegra_clk_pll_a_out0, + tegra_clk_pll_a1, tegra_clk_pll_c, tegra_clk_pll_c2, tegra_clk_pll_c3, tegra_clk_pll_c4, + tegra_clk_pll_c4_out0, + tegra_clk_pll_c4_out1, + tegra_clk_pll_c4_out2, + tegra_clk_pll_c4_out3, tegra_clk_pll_c_out1, tegra_clk_pll_d, tegra_clk_pll_d2, @@ -140,19 +163,29 @@ enum clk_id { tegra_clk_pll_d_out0, tegra_clk_pll_dp, tegra_clk_pll_e_out0, + tegra_clk_pll_g_ref, tegra_clk_pll_m, tegra_clk_pll_m_out1, + tegra_clk_pll_mb, tegra_clk_pll_p, tegra_clk_pll_p_out1, tegra_clk_pll_p_out2, tegra_clk_pll_p_out2_int, tegra_clk_pll_p_out3, tegra_clk_pll_p_out4, + tegra_clk_pll_p_out4_cpu, tegra_clk_pll_p_out5, + tegra_clk_pll_p_out_hsio, + tegra_clk_pll_p_out_xusb, + tegra_clk_pll_p_out_cpu, + tegra_clk_pll_p_out_adsp, tegra_clk_pll_ref, tegra_clk_pll_re_out, tegra_clk_pll_re_vco, tegra_clk_pll_u, + tegra_clk_pll_u_out, + tegra_clk_pll_u_out1, + tegra_clk_pll_u_out2, tegra_clk_pll_u_12m, tegra_clk_pll_u_480m, tegra_clk_pll_u_48m, @@ -160,53 +193,80 @@ enum clk_id { tegra_clk_pll_x, tegra_clk_pll_x_out0, tegra_clk_pwm, + tegra_clk_qspi, tegra_clk_rtc, tegra_clk_sata, + tegra_clk_sata_8, tegra_clk_sata_cold, tegra_clk_sata_oob, + tegra_clk_sata_oob_8, tegra_clk_sbc1, tegra_clk_sbc1_8, + tegra_clk_sbc1_9, tegra_clk_sbc2, tegra_clk_sbc2_8, + tegra_clk_sbc2_9, tegra_clk_sbc3, tegra_clk_sbc3_8, + tegra_clk_sbc3_9, tegra_clk_sbc4, tegra_clk_sbc4_8, + tegra_clk_sbc4_9, tegra_clk_sbc5, tegra_clk_sbc5_8, tegra_clk_sbc6, tegra_clk_sbc6_8, tegra_clk_sclk, + tegra_clk_sdmmc_legacy, tegra_clk_sdmmc1, tegra_clk_sdmmc1_8, + tegra_clk_sdmmc1_9, tegra_clk_sdmmc2, tegra_clk_sdmmc2_8, + tegra_clk_sdmmc2_9, tegra_clk_sdmmc3, tegra_clk_sdmmc3_8, + tegra_clk_sdmmc3_9, tegra_clk_sdmmc4, tegra_clk_sdmmc4_8, + tegra_clk_sdmmc4_9, tegra_clk_se, tegra_clk_soc_therm, + tegra_clk_soc_therm_8, tegra_clk_sor0, tegra_clk_sor0_lvds, + tegra_clk_sor1, + tegra_clk_sor1_brick, + tegra_clk_sor1_src, tegra_clk_spdif, tegra_clk_spdif_2x, tegra_clk_spdif_in, + tegra_clk_spdif_in_8, tegra_clk_spdif_in_sync, tegra_clk_spdif_mux, tegra_clk_spdif_out, tegra_clk_timer, tegra_clk_trace, tegra_clk_tsec, + tegra_clk_tsec_8, + tegra_clk_tsecb, tegra_clk_tsensor, tegra_clk_tvdac, tegra_clk_tvo, tegra_clk_uarta, + tegra_clk_uarta_8, tegra_clk_uartb, + tegra_clk_uartb_8, tegra_clk_uartc, + tegra_clk_uartc_8, tegra_clk_uartd, + tegra_clk_uartd_8, tegra_clk_uarte, + tegra_clk_uarte_8, + tegra_clk_uartape, tegra_clk_usb2, + tegra_clk_usb2_hsic_trk, + tegra_clk_usb2_trk, tegra_clk_usb3, tegra_clk_usbd, tegra_clk_vcp, @@ -216,22 +276,35 @@ enum clk_id { tegra_clk_vi, tegra_clk_vi_8, tegra_clk_vi_9, + tegra_clk_vi_10, + tegra_clk_vi_i2c, tegra_clk_vic03, + tegra_clk_vic03_8, tegra_clk_vim2_clk, tegra_clk_vimclk_sync, tegra_clk_vi_sensor, - tegra_clk_vi_sensor2, tegra_clk_vi_sensor_8, + tegra_clk_vi_sensor_9, + tegra_clk_vi_sensor2, + tegra_clk_vi_sensor2_8, tegra_clk_xusb_dev, tegra_clk_xusb_dev_src, + tegra_clk_xusb_dev_src_8, tegra_clk_xusb_falcon_src, + tegra_clk_xusb_falcon_src_8, tegra_clk_xusb_fs_src, + tegra_clk_xusb_gate, tegra_clk_xusb_host, tegra_clk_xusb_host_src, + tegra_clk_xusb_host_src_8, tegra_clk_xusb_hs_src, + tegra_clk_xusb_hs_src_4, tegra_clk_xusb_ss, tegra_clk_xusb_ss_src, + tegra_clk_xusb_ss_src_8, tegra_clk_xusb_ss_div2, + tegra_clk_xusb_ssp_src, + tegra_clk_sclk_mux, tegra_clk_max, }; diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index d6d4ecb88e94..a534bfab30b3 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -65,6 +65,7 @@ #define PLLE_BASE_DIVN_WIDTH 8 #define PLLE_BASE_DIVM_SHIFT 0 #define PLLE_BASE_DIVM_WIDTH 8 +#define PLLE_BASE_ENABLE BIT(31) #define PLLE_MISC_SETUP_BASE_SHIFT 16 #define PLLE_MISC_SETUP_BASE_MASK (0xffff << PLLE_MISC_SETUP_BASE_SHIFT) @@ -102,6 +103,7 @@ #define PLLE_AUX_SEQ_ENABLE BIT(24) #define PLLE_AUX_SEQ_START_STATE BIT(25) #define PLLE_AUX_PLLRE_SEL BIT(28) +#define PLLE_AUX_SS_SEQ_INCLUDE BIT(31) #define XUSBIO_PLL_CFG0 0x51c #define XUSBIO_PLL_CFG0_PADPLL_RESET_SWCTL BIT(0) @@ -187,17 +189,23 @@ #define pll_readl_base(p) pll_readl(p->params->base_reg, p) #define pll_readl_misc(p) pll_readl(p->params->misc_reg, p) #define pll_override_readl(offset, p) readl_relaxed(p->pmc + offset) +#define pll_readl_sdm_din(p) pll_readl(p->params->sdm_din_reg, p) +#define pll_readl_sdm_ctrl(p) pll_readl(p->params->sdm_ctrl_reg, p) #define pll_writel(val, offset, p) writel_relaxed(val, p->clk_base + offset) #define pll_writel_base(val, p) pll_writel(val, p->params->base_reg, p) #define pll_writel_misc(val, p) pll_writel(val, p->params->misc_reg, p) #define pll_override_writel(val, offset, p) writel(val, p->pmc + offset) +#define pll_writel_sdm_din(val, p) pll_writel(val, p->params->sdm_din_reg, p) +#define pll_writel_sdm_ctrl(val, p) pll_writel(val, p->params->sdm_ctrl_reg, p) #define mask(w) ((1 << (w)) - 1) #define divm_mask(p) mask(p->params->div_nmp->divm_width) #define divn_mask(p) mask(p->params->div_nmp->divn_width) #define divp_mask(p) (p->params->flags & TEGRA_PLLU ? PLLU_POST_DIVP_MASK :\ mask(p->params->div_nmp->divp_width)) +#define sdm_din_mask(p) p->params->sdm_din_mask +#define sdm_en_mask(p) p->params->sdm_ctrl_en_mask #define divm_shift(p) (p)->params->div_nmp->divm_shift #define divn_shift(p) (p)->params->div_nmp->divn_shift @@ -211,6 +219,9 @@ #define divn_max(p) (divn_mask(p)) #define divp_max(p) (1 << (divp_mask(p))) +#define sdin_din_to_data(din) ((u16)((din) ? : 0xFFFFU)) +#define sdin_data_to_din(dat) (((dat) == 0xFFFFU) ? 0 : (s16)dat) + static struct div_nmp default_nmp = { .divn_shift = PLL_BASE_DIVN_SHIFT, .divn_width = PLL_BASE_DIVN_WIDTH, @@ -269,6 +280,11 @@ static int clk_pll_wait_for_lock(struct tegra_clk_pll *pll) return -1; } +int tegra_pll_wait_for_lock(struct tegra_clk_pll *pll) +{ + return clk_pll_wait_for_lock(pll); +} + static int clk_pll_is_enabled(struct clk_hw *hw) { struct tegra_clk_pll *pll = to_clk_pll(hw); @@ -290,6 +306,19 @@ static void _clk_pll_enable(struct clk_hw *hw) struct tegra_clk_pll *pll = to_clk_pll(hw); u32 val; + if (pll->params->iddq_reg) { + val = pll_readl(pll->params->iddq_reg, pll); + val &= ~BIT(pll->params->iddq_bit_idx); + pll_writel(val, pll->params->iddq_reg, pll); + udelay(2); + } + + if (pll->params->reset_reg) { + val = pll_readl(pll->params->reset_reg, pll); + val &= ~BIT(pll->params->reset_bit_idx); + pll_writel(val, pll->params->reset_reg, pll); + } + clk_pll_enable_lock(pll); val = pll_readl_base(pll); @@ -321,6 +350,19 @@ static void _clk_pll_disable(struct clk_hw *hw) val &= ~PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE; writel_relaxed(val, pll->pmc + PMC_PLLP_WB0_OVERRIDE); } + + if (pll->params->reset_reg) { + val = pll_readl(pll->params->reset_reg, pll); + val |= BIT(pll->params->reset_bit_idx); + pll_writel(val, pll->params->reset_reg, pll); + } + + if (pll->params->iddq_reg) { + val = pll_readl(pll->params->iddq_reg, pll); + val |= BIT(pll->params->iddq_bit_idx); + pll_writel(val, pll->params->iddq_reg, pll); + udelay(2); + } } static int clk_pll_enable(struct clk_hw *hw) @@ -359,7 +401,7 @@ static void clk_pll_disable(struct clk_hw *hw) static int _p_div_to_hw(struct clk_hw *hw, u8 p_div) { struct tegra_clk_pll *pll = to_clk_pll(hw); - struct pdiv_map *p_tohw = pll->params->pdiv_tohw; + const struct pdiv_map *p_tohw = pll->params->pdiv_tohw; if (p_tohw) { while (p_tohw->pdiv) { @@ -372,10 +414,15 @@ static int _p_div_to_hw(struct clk_hw *hw, u8 p_div) return -EINVAL; } +int tegra_pll_p_div_to_hw(struct tegra_clk_pll *pll, u8 p_div) +{ + return _p_div_to_hw(&pll->hw, p_div); +} + static int _hw_to_p_div(struct clk_hw *hw, u8 p_div_hw) { struct tegra_clk_pll *pll = to_clk_pll(hw); - struct pdiv_map *p_tohw = pll->params->pdiv_tohw; + const struct pdiv_map *p_tohw = pll->params->pdiv_tohw; if (p_tohw) { while (p_tohw->pdiv) { @@ -395,6 +442,7 @@ static int _get_table_rate(struct clk_hw *hw, { struct tegra_clk_pll *pll = to_clk_pll(hw); struct tegra_clk_pll_freq_table *sel; + int p; for (sel = pll->params->freq_table; sel->input_rate != 0; sel++) if (sel->input_rate == parent_rate && @@ -404,12 +452,21 @@ static int _get_table_rate(struct clk_hw *hw, if (sel->input_rate == 0) return -EINVAL; + if (pll->params->pdiv_tohw) { + p = _p_div_to_hw(hw, sel->p); + if (p < 0) + return p; + } else { + p = ilog2(sel->p); + } + cfg->input_rate = sel->input_rate; cfg->output_rate = sel->output_rate; cfg->m = sel->m; cfg->n = sel->n; - cfg->p = sel->p; + cfg->p = p; cfg->cpcon = sel->cpcon; + cfg->sdm_data = sel->sdm_data; return 0; } @@ -439,7 +496,7 @@ static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, /* * PLL_P_OUT1 rate is not listed in PLLA table */ - cfreq = parent_rate/(parent_rate/1000000); + cfreq = parent_rate / (parent_rate / 1000000); break; default: pr_err("%s Unexpected reference rate %lu\n", @@ -476,6 +533,42 @@ static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, return 0; } +/* + * SDM (Sigma Delta Modulator) divisor is 16-bit 2's complement signed number + * within (-2^12 ... 2^12-1) range. Represented in PLL data structure as + * unsigned 16-bit value, with "0" divisor mapped to 0xFFFF. Data "0" is used + * to indicate that SDM is disabled. + * + * Effective ndiv value when SDM is enabled: ndiv + 1/2 + sdm_din/2^13 + */ +static void clk_pll_set_sdm_data(struct clk_hw *hw, + struct tegra_clk_pll_freq_table *cfg) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + u32 val; + bool enabled; + + if (!pll->params->sdm_din_reg) + return; + + if (cfg->sdm_data) { + val = pll_readl_sdm_din(pll) & (~sdm_din_mask(pll)); + val |= sdin_data_to_din(cfg->sdm_data) & sdm_din_mask(pll); + pll_writel_sdm_din(val, pll); + } + + val = pll_readl_sdm_ctrl(pll); + enabled = (val & sdm_en_mask(pll)); + + if (cfg->sdm_data == 0 && enabled) + val &= ~pll->params->sdm_ctrl_en_mask; + + if (cfg->sdm_data != 0 && !enabled) + val |= pll->params->sdm_ctrl_en_mask; + + pll_writel_sdm_ctrl(val, pll); +} + static void _update_pll_mnp(struct tegra_clk_pll *pll, struct tegra_clk_pll_freq_table *cfg) { @@ -483,7 +576,7 @@ static void _update_pll_mnp(struct tegra_clk_pll *pll, struct tegra_clk_pll_params *params = pll->params; struct div_nmp *div_nmp = params->div_nmp; - if ((params->flags & TEGRA_PLLM) && + if ((params->flags & (TEGRA_PLLM | TEGRA_PLLMB)) && (pll_override_readl(PMC_PLLP_WB0_OVERRIDE, pll) & PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE)) { val = pll_override_readl(params->pmc_divp_reg, pll); @@ -508,6 +601,8 @@ static void _update_pll_mnp(struct tegra_clk_pll *pll, (cfg->p << divp_shift(pll)); pll_writel_base(val, pll); + + clk_pll_set_sdm_data(&pll->hw, cfg); } } @@ -518,7 +613,7 @@ static void _get_pll_mnp(struct tegra_clk_pll *pll, struct tegra_clk_pll_params *params = pll->params; struct div_nmp *div_nmp = params->div_nmp; - if ((params->flags & TEGRA_PLLM) && + if ((params->flags & (TEGRA_PLLM | TEGRA_PLLMB)) && (pll_override_readl(PMC_PLLP_WB0_OVERRIDE, pll) & PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE)) { val = pll_override_readl(params->pmc_divp_reg, pll); @@ -533,6 +628,14 @@ static void _get_pll_mnp(struct tegra_clk_pll *pll, cfg->m = (val >> div_nmp->divm_shift) & divm_mask(pll); cfg->n = (val >> div_nmp->divn_shift) & divn_mask(pll); cfg->p = (val >> div_nmp->divp_shift) & divp_mask(pll); + + if (pll->params->sdm_din_reg) { + if (sdm_en_mask(pll) & pll_readl_sdm_ctrl(pll)) { + val = pll_readl_sdm_din(pll); + val &= sdm_din_mask(pll); + cfg->sdm_data = sdin_din_to_data(val); + } + } } } @@ -560,16 +663,51 @@ static void _update_pll_cpcon(struct tegra_clk_pll *pll, pll_writel_misc(val, pll); } +static void pll_clk_start_ss(struct tegra_clk_pll *pll) +{ + if (pll->params->defaults_set && pll->params->ssc_ctrl_reg) { + u32 val = pll_readl(pll->params->ssc_ctrl_reg, pll); + + val |= pll->params->ssc_ctrl_en_mask; + pll_writel(val, pll->params->ssc_ctrl_reg, pll); + } +} + +static void pll_clk_stop_ss(struct tegra_clk_pll *pll) +{ + if (pll->params->defaults_set && pll->params->ssc_ctrl_reg) { + u32 val = pll_readl(pll->params->ssc_ctrl_reg, pll); + + val &= ~pll->params->ssc_ctrl_en_mask; + pll_writel(val, pll->params->ssc_ctrl_reg, pll); + } +} + static int _program_pll(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, unsigned long rate) { struct tegra_clk_pll *pll = to_clk_pll(hw); + struct tegra_clk_pll_freq_table old_cfg; int state, ret = 0; state = clk_pll_is_enabled(hw); - if (state) + _get_pll_mnp(pll, &old_cfg); + + if (state && pll->params->defaults_set && pll->params->dyn_ramp && + (cfg->m == old_cfg.m) && (cfg->p == old_cfg.p)) { + ret = pll->params->dyn_ramp(pll, cfg); + if (!ret) + return 0; + } + + if (state) { + pll_clk_stop_ss(pll); _clk_pll_disable(hw); + } + + if (!pll->params->defaults_set && pll->params->set_defaults) + pll->params->set_defaults(pll); _update_pll_mnp(pll, cfg); @@ -579,6 +717,7 @@ static int _program_pll(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, if (state) { _clk_pll_enable(hw); ret = clk_pll_wait_for_lock(pll); + pll_clk_start_ss(pll); } return ret; @@ -603,7 +742,7 @@ static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, } if (_get_table_rate(hw, &cfg, rate, parent_rate) && - _calc_rate(hw, &cfg, rate, parent_rate)) { + pll->params->calc_rate(hw, &cfg, rate, parent_rate)) { pr_err("%s: Failed to set %s rate %lu\n", __func__, clk_hw_get_name(hw), rate); WARN_ON(1); @@ -613,8 +752,11 @@ static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, spin_lock_irqsave(pll->lock, flags); _get_pll_mnp(pll, &old_cfg); + if (pll->params->flags & TEGRA_PLL_VCO_OUT) + cfg.p = old_cfg.p; - if (old_cfg.m != cfg.m || old_cfg.n != cfg.n || old_cfg.p != cfg.p) + if (old_cfg.m != cfg.m || old_cfg.n != cfg.n || old_cfg.p != cfg.p || + old_cfg.sdm_data != cfg.sdm_data) ret = _program_pll(hw, &cfg, rate); if (pll->lock) @@ -629,15 +771,15 @@ static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, struct tegra_clk_pll *pll = to_clk_pll(hw); struct tegra_clk_pll_freq_table cfg; - if (pll->params->flags & TEGRA_PLL_FIXED) + if (pll->params->flags & TEGRA_PLL_FIXED) { + /* PLLM/MB are used for memory; we do not change rate */ + if (pll->params->flags & (TEGRA_PLLM | TEGRA_PLLMB)) + return clk_hw_get_rate(hw); return pll->params->fixed_rate; - - /* PLLM is used for memory; we do not change rate */ - if (pll->params->flags & TEGRA_PLLM) - return clk_hw_get_rate(hw); + } if (_get_table_rate(hw, &cfg, rate, *prate) && - _calc_rate(hw, &cfg, rate, *prate)) + pll->params->calc_rate(hw, &cfg, rate, *prate)) return -EINVAL; return cfg.output_rate; @@ -658,6 +800,7 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, return parent_rate; if ((pll->params->flags & TEGRA_PLL_FIXED) && + !(pll->params->flags & (TEGRA_PLLM | TEGRA_PLLMB)) && !(val & PLL_BASE_OVERRIDE)) { struct tegra_clk_pll_freq_table sel; if (_get_table_rate(hw, &sel, pll->params->fixed_rate, @@ -671,12 +814,20 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, _get_pll_mnp(pll, &cfg); - pdiv = _hw_to_p_div(hw, cfg.p); - if (pdiv < 0) { - WARN_ON(1); + if (pll->params->flags & TEGRA_PLL_VCO_OUT) { pdiv = 1; + } else { + pdiv = _hw_to_p_div(hw, cfg.p); + if (pdiv < 0) { + WARN(1, "Clock %s has invalid pdiv value : 0x%x\n", + clk_hw_get_name(hw), cfg.p); + pdiv = 1; + } } + if (pll->params->set_gain) + pll->params->set_gain(&cfg); + cfg.m *= pdiv; rate *= cfg.n; @@ -816,19 +967,65 @@ const struct clk_ops tegra_clk_plle_ops = { .enable = clk_plle_enable, }; -#if defined(CONFIG_ARCH_TEGRA_114_SOC) || \ - defined(CONFIG_ARCH_TEGRA_124_SOC) || \ - defined(CONFIG_ARCH_TEGRA_132_SOC) - static int _pll_fixed_mdiv(struct tegra_clk_pll_params *pll_params, unsigned long parent_rate) { + u16 mdiv = parent_rate / pll_params->cf_min; + + if (pll_params->flags & TEGRA_MDIV_NEW) + return (!pll_params->mdiv_default ? mdiv : + min(mdiv, pll_params->mdiv_default)); + + if (pll_params->mdiv_default) + return pll_params->mdiv_default; + if (parent_rate > pll_params->cf_max) return 2; else return 1; } +static int _calc_dynamic_ramp_rate(struct clk_hw *hw, + struct tegra_clk_pll_freq_table *cfg, + unsigned long rate, unsigned long parent_rate) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned int p; + int p_div; + + if (!rate) + return -EINVAL; + + p = DIV_ROUND_UP(pll->params->vco_min, rate); + cfg->m = _pll_fixed_mdiv(pll->params, parent_rate); + cfg->output_rate = rate * p; + cfg->n = cfg->output_rate * cfg->m / parent_rate; + cfg->input_rate = parent_rate; + + p_div = _p_div_to_hw(hw, p); + if (p_div < 0) + return p_div; + + cfg->p = p_div; + + if (cfg->n > divn_max(pll) || cfg->output_rate > pll->params->vco_max) + return -EINVAL; + + return 0; +} + +#if defined(CONFIG_ARCH_TEGRA_114_SOC) || \ + defined(CONFIG_ARCH_TEGRA_124_SOC) || \ + defined(CONFIG_ARCH_TEGRA_132_SOC) || \ + defined(CONFIG_ARCH_TEGRA_210_SOC) + +u16 tegra_pll_get_fixed_mdiv(struct clk_hw *hw, unsigned long input_rate) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + + return (u16)_pll_fixed_mdiv(pll->params, input_rate); +} + static unsigned long _clip_vco_min(unsigned long vco_min, unsigned long parent_rate) { @@ -871,86 +1068,12 @@ static int _setup_dynamic_ramp(struct tegra_clk_pll_params *pll_params, return 0; } -static int clk_pll_iddq_enable(struct clk_hw *hw) -{ - struct tegra_clk_pll *pll = to_clk_pll(hw); - unsigned long flags = 0; - - u32 val; - int ret; - - if (pll->lock) - spin_lock_irqsave(pll->lock, flags); - - val = pll_readl(pll->params->iddq_reg, pll); - val &= ~BIT(pll->params->iddq_bit_idx); - pll_writel(val, pll->params->iddq_reg, pll); - udelay(2); - - _clk_pll_enable(hw); - - ret = clk_pll_wait_for_lock(pll); - - if (pll->lock) - spin_unlock_irqrestore(pll->lock, flags); - - return 0; -} - -static void clk_pll_iddq_disable(struct clk_hw *hw) -{ - struct tegra_clk_pll *pll = to_clk_pll(hw); - unsigned long flags = 0; - u32 val; - - if (pll->lock) - spin_lock_irqsave(pll->lock, flags); - - _clk_pll_disable(hw); - - val = pll_readl(pll->params->iddq_reg, pll); - val |= BIT(pll->params->iddq_bit_idx); - pll_writel(val, pll->params->iddq_reg, pll); - udelay(2); - - if (pll->lock) - spin_unlock_irqrestore(pll->lock, flags); -} - -static int _calc_dynamic_ramp_rate(struct clk_hw *hw, - struct tegra_clk_pll_freq_table *cfg, - unsigned long rate, unsigned long parent_rate) -{ - struct tegra_clk_pll *pll = to_clk_pll(hw); - unsigned int p; - int p_div; - - if (!rate) - return -EINVAL; - - p = DIV_ROUND_UP(pll->params->vco_min, rate); - cfg->m = _pll_fixed_mdiv(pll->params, parent_rate); - cfg->output_rate = rate * p; - cfg->n = cfg->output_rate * cfg->m / parent_rate; - - p_div = _p_div_to_hw(hw, p); - if (p_div < 0) - return p_div; - else - cfg->p = p_div; - - if (cfg->n > divn_max(pll) || cfg->output_rate > pll->params->vco_max) - return -EINVAL; - - return 0; -} - static int _pll_ramp_calc_pll(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, unsigned long rate, unsigned long parent_rate) { struct tegra_clk_pll *pll = to_clk_pll(hw); - int err = 0, p_div; + int err = 0; err = _get_table_rate(hw, cfg, rate, parent_rate); if (err < 0) @@ -961,11 +1084,6 @@ static int _pll_ramp_calc_pll(struct clk_hw *hw, err = -EINVAL; goto out; } - p_div = _p_div_to_hw(hw, cfg->p); - if (p_div < 0) - return p_div; - else - cfg->p = p_div; } if (cfg->p > pll->params->max_p) @@ -991,6 +1109,8 @@ static int clk_pllxc_set_rate(struct clk_hw *hw, unsigned long rate, spin_lock_irqsave(pll->lock, flags); _get_pll_mnp(pll, &old_cfg); + if (pll->params->flags & TEGRA_PLL_VCO_OUT) + cfg.p = old_cfg.p; if (old_cfg.m != cfg.m || old_cfg.n != cfg.n || old_cfg.p != cfg.p) ret = _program_pll(hw, &cfg, rate); @@ -1004,6 +1124,7 @@ static int clk_pllxc_set_rate(struct clk_hw *hw, unsigned long rate, static long clk_pll_ramp_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) { + struct tegra_clk_pll *pll = to_clk_pll(hw); struct tegra_clk_pll_freq_table cfg; int ret, p_div; u64 output_rate = *prate; @@ -1016,46 +1137,15 @@ static long clk_pll_ramp_round_rate(struct clk_hw *hw, unsigned long rate, if (p_div < 0) return p_div; + if (pll->params->set_gain) + pll->params->set_gain(&cfg); + output_rate *= cfg.n; do_div(output_rate, cfg.m * p_div); return output_rate; } -static int clk_pllm_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) -{ - struct tegra_clk_pll_freq_table cfg; - struct tegra_clk_pll *pll = to_clk_pll(hw); - unsigned long flags = 0; - int state, ret = 0; - - if (pll->lock) - spin_lock_irqsave(pll->lock, flags); - - state = clk_pll_is_enabled(hw); - if (state) { - if (rate != clk_get_rate(hw->clk)) { - pr_err("%s: Cannot change active PLLM\n", __func__); - ret = -EINVAL; - goto out; - } - goto out; - } - - ret = _pll_ramp_calc_pll(hw, &cfg, rate, parent_rate); - if (ret < 0) - goto out; - - _update_pll_mnp(pll, &cfg); - -out: - if (pll->lock) - spin_unlock_irqrestore(pll->lock, flags); - - return ret; -} - static void _pllcx_strobe(struct tegra_clk_pll *pll) { u32 val; @@ -1445,6 +1535,17 @@ static struct clk *_tegra_clk_register_pll(struct tegra_clk_pll *pll, init.parent_names = (parent_name ? &parent_name : NULL); init.num_parents = (parent_name ? 1 : 0); + /* Default to _calc_rate if unspecified */ + if (!pll->params->calc_rate) { + if (pll->params->flags & TEGRA_PLLM) + pll->params->calc_rate = _calc_dynamic_ramp_rate; + else + pll->params->calc_rate = _calc_rate; + } + + if (pll->params->set_defaults) + pll->params->set_defaults(pll); + /* Data in .init is copied by clk_register(), so stack variable OK */ pll->hw.init = &init; @@ -1460,7 +1561,7 @@ struct clk *tegra_clk_register_pll(const char *name, const char *parent_name, struct clk *clk; pll_params->flags |= TEGRA_PLL_BYPASS; - pll_params->flags |= TEGRA_PLL_HAS_LOCK_ENABLE; + pll = _tegra_init_pll(clk_base, pmc, pll_params, lock); if (IS_ERR(pll)) return ERR_CAST(pll); @@ -1490,8 +1591,7 @@ struct clk *tegra_clk_register_plle(const char *name, const char *parent_name, struct tegra_clk_pll *pll; struct clk *clk; - pll_params->flags |= TEGRA_PLL_LOCK_MISC | TEGRA_PLL_BYPASS; - pll_params->flags |= TEGRA_PLL_HAS_LOCK_ENABLE; + pll_params->flags |= TEGRA_PLL_BYPASS; if (!pll_params->div_nmp) pll_params->div_nmp = &pll_e_nmp; @@ -1510,25 +1610,17 @@ struct clk *tegra_clk_register_plle(const char *name, const char *parent_name, #if defined(CONFIG_ARCH_TEGRA_114_SOC) || \ defined(CONFIG_ARCH_TEGRA_124_SOC) || \ - defined(CONFIG_ARCH_TEGRA_132_SOC) + defined(CONFIG_ARCH_TEGRA_132_SOC) || \ + defined(CONFIG_ARCH_TEGRA_210_SOC) static const struct clk_ops tegra_clk_pllxc_ops = { .is_enabled = clk_pll_is_enabled, - .enable = clk_pll_iddq_enable, - .disable = clk_pll_iddq_disable, + .enable = clk_pll_enable, + .disable = clk_pll_disable, .recalc_rate = clk_pll_recalc_rate, .round_rate = clk_pll_ramp_round_rate, .set_rate = clk_pllxc_set_rate, }; -static const struct clk_ops tegra_clk_pllm_ops = { - .is_enabled = clk_pll_is_enabled, - .enable = clk_pll_iddq_enable, - .disable = clk_pll_iddq_disable, - .recalc_rate = clk_pll_recalc_rate, - .round_rate = clk_pll_ramp_round_rate, - .set_rate = clk_pllm_set_rate, -}; - static const struct clk_ops tegra_clk_pllc_ops = { .is_enabled = clk_pll_is_enabled, .enable = clk_pllc_enable, @@ -1540,8 +1632,8 @@ static const struct clk_ops tegra_clk_pllc_ops = { static const struct clk_ops tegra_clk_pllre_ops = { .is_enabled = clk_pll_is_enabled, - .enable = clk_pll_iddq_enable, - .disable = clk_pll_iddq_disable, + .enable = clk_pll_enable, + .disable = clk_pll_disable, .recalc_rate = clk_pllre_recalc_rate, .round_rate = clk_pllre_round_rate, .set_rate = clk_pllre_set_rate, @@ -1564,7 +1656,6 @@ struct clk *tegra_clk_register_pllxc(const char *name, const char *parent_name, struct tegra_clk_pll *pll; struct clk *clk, *parent; unsigned long parent_rate; - int err; u32 val, val_iddq; parent = __clk_lookup(parent_name); @@ -1581,21 +1672,33 @@ struct clk *tegra_clk_register_pllxc(const char *name, const char *parent_name, pll_params->vco_min = _clip_vco_min(pll_params->vco_min, parent_rate); - err = _setup_dynamic_ramp(pll_params, clk_base, parent_rate); - if (err) - return ERR_PTR(err); + if (pll_params->adjust_vco) + pll_params->vco_min = pll_params->adjust_vco(pll_params, + parent_rate); - val = readl_relaxed(clk_base + pll_params->base_reg); - val_iddq = readl_relaxed(clk_base + pll_params->iddq_reg); + /* + * If the pll has a set_defaults callback, it will take care of + * configuring dynamic ramping and setting IDDQ in that path. + */ + if (!pll_params->set_defaults) { + int err; - if (val & PLL_BASE_ENABLE) - WARN_ON(val_iddq & BIT(pll_params->iddq_bit_idx)); - else { - val_iddq |= BIT(pll_params->iddq_bit_idx); - writel_relaxed(val_iddq, clk_base + pll_params->iddq_reg); + err = _setup_dynamic_ramp(pll_params, clk_base, parent_rate); + if (err) + return ERR_PTR(err); + + val = readl_relaxed(clk_base + pll_params->base_reg); + val_iddq = readl_relaxed(clk_base + pll_params->iddq_reg); + + if (val & PLL_BASE_ENABLE) + WARN_ON(val_iddq & BIT(pll_params->iddq_bit_idx)); + else { + val_iddq |= BIT(pll_params->iddq_bit_idx); + writel_relaxed(val_iddq, + clk_base + pll_params->iddq_reg); + } } - pll_params->flags |= TEGRA_PLL_HAS_LOCK_ENABLE; pll = _tegra_init_pll(clk_base, pmc, pll_params, lock); if (IS_ERR(pll)) return ERR_CAST(pll); @@ -1618,10 +1721,12 @@ struct clk *tegra_clk_register_pllre(const char *name, const char *parent_name, struct tegra_clk_pll *pll; struct clk *clk; - pll_params->flags |= TEGRA_PLL_HAS_LOCK_ENABLE | TEGRA_PLL_LOCK_MISC; - pll_params->vco_min = _clip_vco_min(pll_params->vco_min, parent_rate); + if (pll_params->adjust_vco) + pll_params->vco_min = pll_params->adjust_vco(pll_params, + parent_rate); + pll = _tegra_init_pll(clk_base, pmc, pll_params, lock); if (IS_ERR(pll)) return ERR_CAST(pll); @@ -1630,7 +1735,8 @@ struct clk *tegra_clk_register_pllre(const char *name, const char *parent_name, val = pll_readl_base(pll); if (val & PLL_BASE_ENABLE) - WARN_ON(val & pll_params->iddq_bit_idx); + WARN_ON(readl_relaxe |