Merge branches 'clk-gpio-flags', 'clk-tegra', 'clk-rockchip', 'clk-sprd' and 'clk-pxa' into clk-next
- Make gpio gate clks propagate rate setting up to parent * clk-gpio-flags: clk: clk-gpio: propagate rate change to parent * clk-tegra: (23 commits) clk: tegra: Use match_string() helper to simplify the code clk: tegra: Fix build error without CONFIG_PM_SLEEP clk: tegra: Add missing stubs for the case of !CONFIG_PM_SLEEP clk: tegra: Optimize PLLX restore on Tegra20/30 clk: tegra: Add suspend and resume support on Tegra210 clk: tegra: Share clk and rst register defines with Tegra clock driver clk: tegra: Use fence_udelay() during PLLU init clk: tegra: clk-dfll: Add suspend and resume support clk: tegra: clk-super: Add restore-context support clk: tegra: clk-super: Fix to enable PLLP branches to CPU clk: tegra: periph: Add restore_context support clk: tegra: Support for OSC context save and restore clk: tegra: pll: Save and restore pll context clk: tegra: pllout: Save and restore pllout context clk: tegra: divider: Save and restore divider rate clk: tegra: Reimplement SOR clocks on Tegra210 clk: tegra: Reimplement SOR clock on Tegra124 clk: tegra: Rename sor0_lvds to sor0_out clk: tegra: Move SOR0 implementation to Tegra124 clk: tegra: Remove last remains of TEGRA210_CLK_SOR1_SRC ... * clk-rockchip: clk: rockchip: protect the pclk_usb_grf as critical on px30 clk: rockchip: add video-related niu clocks as critical on px30 clk: rockchip: move px30 critical clocks to correct clock controller clk: rockchip: Add div50 clocks for px30 sdmmc, emmc, sdio and nandc clk: rockchip: Add div50 clock-ids for sdmmc on px30 and nandc clk: rockchip: make clk_half_divider_ops static * clk-sprd: clk: sprd: Use IS_ERR() to validate the return value of syscon_regmap_lookup_by_phandle() * clk-pxa: clk: pxa: fix one of the pxa RTC clocks
This commit is contained in:
commit
dabedfede3
|
@ -280,7 +280,7 @@ static int gpio_clk_driver_probe(struct platform_device *pdev)
|
|||
else
|
||||
clk = clk_register_gpio_gate(&pdev->dev, node->name,
|
||||
parent_names ? parent_names[0] : NULL, gpiod,
|
||||
0);
|
||||
CLK_SET_RATE_PARENT);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
|
|
|
@ -1674,6 +1674,24 @@ static int clk_fetch_parent_index(struct clk_core *core,
|
|||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* clk_hw_get_parent_index - return the index of the parent clock
|
||||
* @hw: clk_hw associated with the clk being consumed
|
||||
*
|
||||
* Fetches and returns the index of parent clock. Returns -EINVAL if the given
|
||||
* clock does not have a current parent.
|
||||
*/
|
||||
int clk_hw_get_parent_index(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_hw *parent = clk_hw_get_parent(hw);
|
||||
|
||||
if (WARN_ON(parent == NULL))
|
||||
return -EINVAL;
|
||||
|
||||
return clk_fetch_parent_index(hw->core, parent->core);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clk_hw_get_parent_index);
|
||||
|
||||
/*
|
||||
* Update the orphan status of @core and all its children.
|
||||
*/
|
||||
|
|
|
@ -459,6 +459,7 @@ struct dummy_clk {
|
|||
};
|
||||
static struct dummy_clk dummy_clks[] __initdata = {
|
||||
DUMMY_CLK(NULL, "pxa27x-gpio", "osc_32_768khz"),
|
||||
DUMMY_CLK(NULL, "pxa-rtc", "osc_32_768khz"),
|
||||
DUMMY_CLK(NULL, "sa1100-rtc", "osc_32_768khz"),
|
||||
DUMMY_CLK("UARTCLK", "pxa2xx-ir", "STUART"),
|
||||
};
|
||||
|
|
|
@ -139,12 +139,11 @@ static int clk_half_divider_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
return 0;
|
||||
}
|
||||
|
||||
const struct clk_ops clk_half_divider_ops = {
|
||||
static const struct clk_ops clk_half_divider_ops = {
|
||||
.recalc_rate = clk_half_divider_recalc_rate,
|
||||
.round_rate = clk_half_divider_round_rate,
|
||||
.set_rate = clk_half_divider_set_rate,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(clk_half_divider_ops);
|
||||
|
||||
/**
|
||||
* Register a clock branch.
|
||||
|
|
|
@ -167,6 +167,10 @@ PNAME(mux_uart5_p) = { "clk_uart5_src", "clk_uart5_np5", "clk_uart5_frac" };
|
|||
PNAME(mux_cif_out_p) = { "xin24m", "dummy_cpll", "npll", "usb480m" };
|
||||
PNAME(mux_dclk_vopb_p) = { "dclk_vopb_src", "dclk_vopb_frac", "xin24m" };
|
||||
PNAME(mux_dclk_vopl_p) = { "dclk_vopl_src", "dclk_vopl_frac", "xin24m" };
|
||||
PNAME(mux_nandc_p) = { "clk_nandc_div", "clk_nandc_div50" };
|
||||
PNAME(mux_sdio_p) = { "clk_sdio_div", "clk_sdio_div50" };
|
||||
PNAME(mux_emmc_p) = { "clk_emmc_div", "clk_emmc_div50" };
|
||||
PNAME(mux_sdmmc_p) = { "clk_sdmmc_div", "clk_sdmmc_div50" };
|
||||
PNAME(mux_gmac_p) = { "clk_gmac_src", "gmac_clkin" };
|
||||
PNAME(mux_gmac_rmii_sel_p) = { "clk_gmac_rx_tx_div20", "clk_gmac_rx_tx_div2" };
|
||||
PNAME(mux_rtc32k_pmu_p) = { "xin32k", "pmu_pvtm_32k", "clk_rtc32k_frac", };
|
||||
|
@ -460,16 +464,40 @@ static struct rockchip_clk_branch px30_clk_branches[] __initdata = {
|
|||
/* PD_MMC_NAND */
|
||||
GATE(HCLK_MMC_NAND, "hclk_mmc_nand", "hclk_peri_pre", 0,
|
||||
PX30_CLKGATE_CON(6), 0, GFLAGS),
|
||||
COMPOSITE(SCLK_NANDC, "clk_nandc", mux_gpll_cpll_npll_p, 0,
|
||||
COMPOSITE(SCLK_NANDC_DIV, "clk_nandc_div", mux_gpll_cpll_npll_p, 0,
|
||||
PX30_CLKSEL_CON(15), 6, 2, MFLAGS, 0, 5, DFLAGS,
|
||||
PX30_CLKGATE_CON(5), 11, GFLAGS),
|
||||
COMPOSITE(SCLK_NANDC_DIV50, "clk_nandc_div50", mux_gpll_cpll_npll_p, 0,
|
||||
PX30_CLKSEL_CON(15), 6, 2, MFLAGS, 8, 5, DFLAGS,
|
||||
PX30_CLKGATE_CON(5), 12, GFLAGS),
|
||||
COMPOSITE_NODIV(SCLK_NANDC, "clk_nandc", mux_nandc_p,
|
||||
CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
|
||||
PX30_CLKSEL_CON(15), 15, 1, MFLAGS,
|
||||
PX30_CLKGATE_CON(5), 13, GFLAGS),
|
||||
|
||||
COMPOSITE(SCLK_SDIO, "clk_sdio", mux_gpll_cpll_npll_xin24m_p, 0,
|
||||
COMPOSITE(SCLK_SDIO_DIV, "clk_sdio_div", mux_gpll_cpll_npll_xin24m_p, 0,
|
||||
PX30_CLKSEL_CON(18), 14, 2, MFLAGS, 0, 8, DFLAGS,
|
||||
PX30_CLKGATE_CON(6), 1, GFLAGS),
|
||||
COMPOSITE_DIV_OFFSET(SCLK_SDIO_DIV50, "clk_sdio_div50",
|
||||
mux_gpll_cpll_npll_xin24m_p, 0,
|
||||
PX30_CLKSEL_CON(18), 14, 2, MFLAGS,
|
||||
PX30_CLKSEL_CON(19), 0, 8, DFLAGS,
|
||||
PX30_CLKGATE_CON(6), 2, GFLAGS),
|
||||
COMPOSITE_NODIV(SCLK_SDIO, "clk_sdio", mux_sdio_p,
|
||||
CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
|
||||
PX30_CLKSEL_CON(19), 15, 1, MFLAGS,
|
||||
PX30_CLKGATE_CON(6), 3, GFLAGS),
|
||||
|
||||
COMPOSITE(SCLK_EMMC, "clk_emmc", mux_gpll_cpll_npll_xin24m_p, 0,
|
||||
COMPOSITE(SCLK_EMMC_DIV, "clk_emmc_div", mux_gpll_cpll_npll_xin24m_p, 0,
|
||||
PX30_CLKSEL_CON(20), 14, 2, MFLAGS, 0, 8, DFLAGS,
|
||||
PX30_CLKGATE_CON(6), 4, GFLAGS),
|
||||
COMPOSITE_DIV_OFFSET(SCLK_EMMC_DIV50, "clk_emmc_div50", mux_gpll_cpll_npll_xin24m_p, 0,
|
||||
PX30_CLKSEL_CON(20), 14, 2, MFLAGS,
|
||||
PX30_CLKSEL_CON(21), 0, 8, DFLAGS,
|
||||
PX30_CLKGATE_CON(6), 5, GFLAGS),
|
||||
COMPOSITE_NODIV(SCLK_EMMC, "clk_emmc", mux_emmc_p,
|
||||
CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
|
||||
PX30_CLKSEL_CON(21), 15, 1, MFLAGS,
|
||||
PX30_CLKGATE_CON(6), 6, GFLAGS),
|
||||
|
||||
COMPOSITE(SCLK_SFC, "clk_sfc", mux_gpll_cpll_p, 0,
|
||||
|
@ -494,8 +522,16 @@ static struct rockchip_clk_branch px30_clk_branches[] __initdata = {
|
|||
/* PD_SDCARD */
|
||||
GATE(0, "hclk_sdmmc_pre", "hclk_peri_pre", 0,
|
||||
PX30_CLKGATE_CON(6), 12, GFLAGS),
|
||||
COMPOSITE(SCLK_SDMMC, "clk_sdmmc", mux_gpll_cpll_npll_xin24m_p, 0,
|
||||
COMPOSITE(SCLK_SDMMC_DIV, "clk_sdmmc_div", mux_gpll_cpll_npll_xin24m_p, 0,
|
||||
PX30_CLKSEL_CON(16), 14, 2, MFLAGS, 0, 8, DFLAGS,
|
||||
PX30_CLKGATE_CON(6), 13, GFLAGS),
|
||||
COMPOSITE_DIV_OFFSET(SCLK_SDMMC_DIV50, "clk_sdmmc_div50", mux_gpll_cpll_npll_xin24m_p, 0,
|
||||
PX30_CLKSEL_CON(16), 14, 2, MFLAGS,
|
||||
PX30_CLKSEL_CON(17), 0, 8, DFLAGS,
|
||||
PX30_CLKGATE_CON(6), 14, GFLAGS),
|
||||
COMPOSITE_NODIV(SCLK_SDMMC, "clk_sdmmc", mux_sdmmc_p,
|
||||
CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
|
||||
PX30_CLKSEL_CON(17), 15, 1, MFLAGS,
|
||||
PX30_CLKGATE_CON(6), 15, GFLAGS),
|
||||
|
||||
/* PD_USB */
|
||||
|
@ -763,29 +799,29 @@ static struct rockchip_clk_branch px30_clk_branches[] __initdata = {
|
|||
GATE(0, "pclk_ddrphy", "pclk_top_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(16), 3, GFLAGS),
|
||||
GATE(PCLK_MIPIDSIPHY, "pclk_mipidsiphy", "pclk_top_pre", 0, PX30_CLKGATE_CON(16), 4, GFLAGS),
|
||||
GATE(PCLK_MIPICSIPHY, "pclk_mipicsiphy", "pclk_top_pre", 0, PX30_CLKGATE_CON(16), 5, GFLAGS),
|
||||
GATE(PCLK_USB_GRF, "pclk_usb_grf", "pclk_top_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(16), 6, GFLAGS),
|
||||
GATE(PCLK_USB_GRF, "pclk_usb_grf", "pclk_top_pre", 0, PX30_CLKGATE_CON(16), 6, GFLAGS),
|
||||
GATE(0, "pclk_cpu_hoost", "pclk_top_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(16), 7, GFLAGS),
|
||||
|
||||
/* PD_VI */
|
||||
GATE(0, "aclk_vi_niu", "aclk_vi_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(4), 15, GFLAGS),
|
||||
GATE(0, "aclk_vi_niu", "aclk_vi_pre", 0, PX30_CLKGATE_CON(4), 15, GFLAGS),
|
||||
GATE(ACLK_CIF, "aclk_cif", "aclk_vi_pre", 0, PX30_CLKGATE_CON(5), 1, GFLAGS),
|
||||
GATE(ACLK_ISP, "aclk_isp", "aclk_vi_pre", 0, PX30_CLKGATE_CON(5), 3, GFLAGS),
|
||||
GATE(0, "hclk_vi_niu", "hclk_vi_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(5), 0, GFLAGS),
|
||||
GATE(0, "hclk_vi_niu", "hclk_vi_pre", 0, PX30_CLKGATE_CON(5), 0, GFLAGS),
|
||||
GATE(HCLK_CIF, "hclk_cif", "hclk_vi_pre", 0, PX30_CLKGATE_CON(5), 2, GFLAGS),
|
||||
GATE(HCLK_ISP, "hclk_isp", "hclk_vi_pre", 0, PX30_CLKGATE_CON(5), 4, GFLAGS),
|
||||
|
||||
/* PD_VO */
|
||||
GATE(0, "aclk_vo_niu", "aclk_vo_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(3), 0, GFLAGS),
|
||||
GATE(0, "aclk_vo_niu", "aclk_vo_pre", 0, PX30_CLKGATE_CON(3), 0, GFLAGS),
|
||||
GATE(ACLK_VOPB, "aclk_vopb", "aclk_vo_pre", 0, PX30_CLKGATE_CON(3), 3, GFLAGS),
|
||||
GATE(ACLK_RGA, "aclk_rga", "aclk_vo_pre", 0, PX30_CLKGATE_CON(3), 7, GFLAGS),
|
||||
GATE(ACLK_VOPL, "aclk_vopl", "aclk_vo_pre", 0, PX30_CLKGATE_CON(3), 5, GFLAGS),
|
||||
|
||||
GATE(0, "hclk_vo_niu", "hclk_vo_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(3), 1, GFLAGS),
|
||||
GATE(0, "hclk_vo_niu", "hclk_vo_pre", 0, PX30_CLKGATE_CON(3), 1, GFLAGS),
|
||||
GATE(HCLK_VOPB, "hclk_vopb", "hclk_vo_pre", 0, PX30_CLKGATE_CON(3), 4, GFLAGS),
|
||||
GATE(HCLK_RGA, "hclk_rga", "hclk_vo_pre", 0, PX30_CLKGATE_CON(3), 8, GFLAGS),
|
||||
GATE(HCLK_VOPL, "hclk_vopl", "hclk_vo_pre", 0, PX30_CLKGATE_CON(3), 6, GFLAGS),
|
||||
|
||||
GATE(0, "pclk_vo_niu", "pclk_vo_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(3), 2, GFLAGS),
|
||||
GATE(0, "pclk_vo_niu", "pclk_vo_pre", 0, PX30_CLKGATE_CON(3), 2, GFLAGS),
|
||||
GATE(PCLK_MIPI_DSI, "pclk_mipi_dsi", "pclk_vo_pre", 0, PX30_CLKGATE_CON(3), 9, GFLAGS),
|
||||
|
||||
/* PD_BUS */
|
||||
|
@ -940,7 +976,7 @@ static struct rockchip_clk_branch px30_clk_pmu_branches[] __initdata = {
|
|||
GATE(0, "pclk_cru_pmu", "pclk_pmu_pre", CLK_IGNORE_UNUSED, PX30_PMU_CLKGATE_CON(0), 8, GFLAGS),
|
||||
};
|
||||
|
||||
static const char *const px30_pmucru_critical_clocks[] __initconst = {
|
||||
static const char *const px30_cru_critical_clocks[] __initconst = {
|
||||
"aclk_bus_pre",
|
||||
"pclk_bus_pre",
|
||||
"hclk_bus_pre",
|
||||
|
@ -950,10 +986,16 @@ static const char *const px30_pmucru_critical_clocks[] __initconst = {
|
|||
"pclk_top_pre",
|
||||
"pclk_pmu_pre",
|
||||
"hclk_usb_niu",
|
||||
"pclk_vo_niu",
|
||||
"aclk_vo_niu",
|
||||
"hclk_vo_niu",
|
||||
"aclk_vi_niu",
|
||||
"hclk_vi_niu",
|
||||
"pll_npll",
|
||||
"usb480m",
|
||||
"clk_uart2",
|
||||
"pclk_uart2",
|
||||
"pclk_usb_grf",
|
||||
};
|
||||
|
||||
static void __init px30_clk_init(struct device_node *np)
|
||||
|
@ -985,6 +1027,9 @@ static void __init px30_clk_init(struct device_node *np)
|
|||
&px30_cpuclk_data, px30_cpuclk_rates,
|
||||
ARRAY_SIZE(px30_cpuclk_rates));
|
||||
|
||||
rockchip_clk_protect_critical(px30_cru_critical_clocks,
|
||||
ARRAY_SIZE(px30_cru_critical_clocks));
|
||||
|
||||
rockchip_register_softrst(np, 12, reg_base + PX30_SOFTRST_CON(0),
|
||||
ROCKCHIP_SOFTRST_HIWORD_MASK);
|
||||
|
||||
|
@ -1017,9 +1062,6 @@ static void __init px30_pmu_clk_init(struct device_node *np)
|
|||
rockchip_clk_register_branches(ctx, px30_clk_pmu_branches,
|
||||
ARRAY_SIZE(px30_clk_pmu_branches));
|
||||
|
||||
rockchip_clk_protect_critical(px30_pmucru_critical_clocks,
|
||||
ARRAY_SIZE(px30_pmucru_critical_clocks));
|
||||
|
||||
rockchip_clk_of_add_provider(np, ctx);
|
||||
}
|
||||
CLK_OF_DECLARE(px30_cru_pmu, "rockchip,px30-pmucru", px30_pmu_clk_init);
|
||||
|
|
|
@ -45,7 +45,7 @@ int sprd_clk_regmap_init(struct platform_device *pdev,
|
|||
|
||||
if (of_find_property(node, "sprd,syscon", NULL)) {
|
||||
regmap = syscon_regmap_lookup_by_phandle(node, "sprd,syscon");
|
||||
if (IS_ERR_OR_NULL(regmap)) {
|
||||
if (IS_ERR(regmap)) {
|
||||
pr_err("%s: failed to get syscon regmap\n", __func__);
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,9 @@ obj-y += clk-tegra-fixed.o
|
|||
obj-y += clk-tegra-super-gen4.o
|
||||
obj-$(CONFIG_TEGRA_CLK_EMC) += clk-emc.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20-emc.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra30.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra20-emc.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_114_SOC) += clk-tegra114.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124.o
|
||||
obj-$(CONFIG_TEGRA_CLK_DFLL) += clk-tegra124-dfll-fcpu.o
|
||||
|
|
|
@ -1487,6 +1487,7 @@ static int dfll_init(struct tegra_dfll *td)
|
|||
td->last_unrounded_rate = 0;
|
||||
|
||||
pm_runtime_enable(td->dev);
|
||||
pm_runtime_irq_safe(td->dev);
|
||||
pm_runtime_get_sync(td->dev);
|
||||
|
||||
dfll_set_mode(td, DFLL_DISABLED);
|
||||
|
@ -1513,6 +1514,61 @@ di_err1:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* tegra_dfll_suspend - check DFLL is disabled
|
||||
* @dev: DFLL device *
|
||||
*
|
||||
* DFLL clock should be disabled by the CPUFreq driver. So, make
|
||||
* sure it is disabled and disable all clocks needed by the DFLL.
|
||||
*/
|
||||
int tegra_dfll_suspend(struct device *dev)
|
||||
{
|
||||
struct tegra_dfll *td = dev_get_drvdata(dev);
|
||||
|
||||
if (dfll_is_running(td)) {
|
||||
dev_err(td->dev, "DFLL still enabled while suspending\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
reset_control_assert(td->dvco_rst);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_dfll_suspend);
|
||||
|
||||
/**
|
||||
* tegra_dfll_resume - reinitialize DFLL on resume
|
||||
* @dev: DFLL instance
|
||||
*
|
||||
* DFLL is disabled and reset during suspend and resume.
|
||||
* So, reinitialize the DFLL IP block back for use.
|
||||
* DFLL clock is enabled later in closed loop mode by CPUFreq
|
||||
* driver before switching its clock source to DFLL output.
|
||||
*/
|
||||
int tegra_dfll_resume(struct device *dev)
|
||||
{
|
||||
struct tegra_dfll *td = dev_get_drvdata(dev);
|
||||
|
||||
reset_control_deassert(td->dvco_rst);
|
||||
|
||||
pm_runtime_get_sync(td->dev);
|
||||
|
||||
dfll_set_mode(td, DFLL_DISABLED);
|
||||
dfll_set_default_params(td);
|
||||
|
||||
if (td->soc->init_clock_trimmers)
|
||||
td->soc->init_clock_trimmers();
|
||||
|
||||
dfll_set_open_loop_config(td);
|
||||
|
||||
dfll_init_out_if(td);
|
||||
|
||||
pm_runtime_put_sync(td->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_dfll_resume);
|
||||
|
||||
/*
|
||||
* DT data fetch
|
||||
*/
|
||||
|
|
|
@ -42,5 +42,7 @@ int tegra_dfll_register(struct platform_device *pdev,
|
|||
struct tegra_dfll_soc_data *tegra_dfll_unregister(struct platform_device *pdev);
|
||||
int tegra_dfll_runtime_suspend(struct device *dev);
|
||||
int tegra_dfll_runtime_resume(struct device *dev);
|
||||
int tegra_dfll_suspend(struct device *dev);
|
||||
int tegra_dfll_resume(struct device *dev);
|
||||
|
||||
#endif /* __DRIVERS_CLK_TEGRA_CLK_DFLL_H */
|
||||
|
|
|
@ -109,10 +109,21 @@ static int clk_frac_div_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void clk_divider_restore_context(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_hw *parent = clk_hw_get_parent(hw);
|
||||
unsigned long parent_rate = clk_hw_get_rate(parent);
|
||||
unsigned long rate = clk_hw_get_rate(hw);
|
||||
|
||||
if (clk_frac_div_set_rate(hw, rate, parent_rate) < 0)
|
||||
WARN_ON(1);
|
||||
}
|
||||
|
||||
const struct clk_ops tegra_clk_frac_div_ops = {
|
||||
.recalc_rate = clk_frac_div_recalc_rate,
|
||||
.set_rate = clk_frac_div_set_rate,
|
||||
.round_rate = clk_frac_div_round_rate,
|
||||
.restore_context = clk_divider_restore_context,
|
||||
};
|
||||
|
||||
struct clk *tegra_clk_register_divider(const char *name,
|
||||
|
|
|
@ -403,20 +403,16 @@ static int load_one_timing_from_dt(struct tegra_clk_emc *tegra,
|
|||
}
|
||||
|
||||
timing->parent_index = 0xff;
|
||||
for (i = 0; i < ARRAY_SIZE(emc_parent_clk_names); i++) {
|
||||
if (!strcmp(emc_parent_clk_names[i],
|
||||
__clk_get_name(timing->parent))) {
|
||||
timing->parent_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (timing->parent_index == 0xff) {
|
||||
i = match_string(emc_parent_clk_names, ARRAY_SIZE(emc_parent_clk_names),
|
||||
__clk_get_name(timing->parent));
|
||||
if (i < 0) {
|
||||
pr_err("timing %pOF: %s is not a valid parent\n",
|
||||
node, __clk_get_name(timing->parent));
|
||||
clk_put(timing->parent);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
timing->parent_index = i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -236,9 +236,9 @@ enum clk_id {
|
|||
tegra_clk_soc_therm,
|
||||
tegra_clk_soc_therm_8,
|
||||
tegra_clk_sor0,
|
||||
tegra_clk_sor0_lvds,
|
||||
tegra_clk_sor0_out,
|
||||
tegra_clk_sor1,
|
||||
tegra_clk_sor1_src,
|
||||
tegra_clk_sor1_out,
|
||||
tegra_clk_spdif,
|
||||
tegra_clk_spdif_2x,
|
||||
tegra_clk_spdif_in,
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -99,6 +100,23 @@ static void clk_periph_disable(struct clk_hw *hw)
|
|||
gate_ops->disable(gate_hw);
|
||||
}
|
||||
|
||||
static void clk_periph_restore_context(struct clk_hw *hw)
|
||||
{
|
||||
struct tegra_clk_periph *periph = to_clk_periph(hw);
|
||||
const struct clk_ops *div_ops = periph->div_ops;
|
||||
struct clk_hw *div_hw = &periph->divider.hw;
|
||||
int parent_id;
|
||||
|
||||
parent_id = clk_hw_get_parent_index(hw);
|
||||
if (WARN_ON(parent_id < 0))
|
||||
return;
|
||||
|
||||
if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
|
||||
div_ops->restore_context(div_hw);
|
||||
|
||||
clk_periph_set_parent(hw, parent_id);
|
||||
}
|
||||
|
||||
const struct clk_ops tegra_clk_periph_ops = {
|
||||
.get_parent = clk_periph_get_parent,
|
||||
.set_parent = clk_periph_set_parent,
|
||||
|
@ -108,6 +126,7 @@ const struct clk_ops tegra_clk_periph_ops = {
|
|||
.is_enabled = clk_periph_is_enabled,
|
||||
.enable = clk_periph_enable,
|
||||
.disable = clk_periph_disable,
|
||||
.restore_context = clk_periph_restore_context,
|
||||
};
|
||||
|
||||
static const struct clk_ops tegra_clk_periph_nodiv_ops = {
|
||||
|
@ -116,6 +135,7 @@ static const struct clk_ops tegra_clk_periph_nodiv_ops = {
|
|||
.is_enabled = clk_periph_is_enabled,
|
||||
.enable = clk_periph_enable,
|
||||
.disable = clk_periph_disable,
|
||||
.restore_context = clk_periph_restore_context,
|
||||
};
|
||||
|
||||
static const struct clk_ops tegra_clk_periph_no_gate_ops = {
|
||||
|
@ -124,6 +144,7 @@ static const struct clk_ops tegra_clk_periph_no_gate_ops = {
|
|||
.recalc_rate = clk_periph_recalc_rate,
|
||||
.round_rate = clk_periph_round_rate,
|
||||
.set_rate = clk_periph_set_rate,
|
||||
.restore_context = clk_periph_restore_context,
|
||||
};
|
||||
|
||||
static struct clk *_tegra_clk_register_periph(const char *name,
|
||||
|
|
|
@ -69,10 +69,19 @@ static void clk_pll_out_disable(struct clk_hw *hw)
|
|||
spin_unlock_irqrestore(pll_out->lock, flags);
|
||||
}
|
||||
|
||||
static void tegra_clk_pll_out_restore_context(struct clk_hw *hw)
|
||||
{
|
||||
if (!__clk_get_enable_count(hw->clk))
|
||||
clk_pll_out_disable(hw);
|
||||
else
|
||||
clk_pll_out_enable(hw);
|
||||
}
|
||||
|
||||
const struct clk_ops tegra_clk_pll_out_ops = {
|
||||
.is_enabled = clk_pll_out_is_enabled,
|
||||
.enable = clk_pll_out_enable,
|
||||
.disable = clk_pll_out_disable,
|
||||
.restore_context = tegra_clk_pll_out_restore_context,
|
||||
};
|
||||
|
||||
struct clk *tegra_clk_register_pll_out(const char *name,
|
||||
|
|
|
@ -1008,6 +1008,27 @@ static unsigned long clk_plle_recalc_rate(struct clk_hw *hw,
|
|||
return rate;
|
||||
}
|
||||
|
||||
static void tegra_clk_pll_restore_context(struct clk_hw *hw)
|
||||
{
|
||||
struct tegra_clk_pll *pll = to_clk_pll(hw);
|
||||
struct clk_hw *parent = clk_hw_get_parent(hw);
|
||||
unsigned long parent_rate = clk_hw_get_rate(parent);
|
||||
unsigned long rate = clk_hw_get_rate(hw);
|
||||
|
||||
if (clk_pll_is_enabled(hw))
|
||||
return;
|
||||
|
||||
if (pll->params->set_defaults)
|
||||
pll->params->set_defaults(pll);
|
||||
|
||||
clk_pll_set_rate(hw, rate, parent_rate);
|
||||
|
||||
if (!__clk_get_enable_count(hw->clk))
|
||||
clk_pll_disable(hw);
|
||||
else
|
||||
clk_pll_enable(hw);
|
||||
}
|
||||
|
||||
const struct clk_ops tegra_clk_pll_ops = {
|
||||
.is_enabled = clk_pll_is_enabled,
|
||||
.enable = clk_pll_enable,
|
||||
|
@ -1015,6 +1036,7 @@ const struct clk_ops tegra_clk_pll_ops = {
|
|||
.recalc_rate = clk_pll_recalc_rate,
|
||||
.round_rate = clk_pll_round_rate,
|
||||
.set_rate = clk_pll_set_rate,
|
||||
.restore_context = tegra_clk_pll_restore_context,
|
||||
};
|
||||
|
||||
const struct clk_ops tegra_clk_plle_ops = {
|
||||
|
@ -1802,6 +1824,27 @@ out:
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void _clk_plle_tegra_init_parent(struct tegra_clk_pll *pll)
|
||||
{
|
||||
u32 val, val_aux;
|
||||
|
||||
/* ensure parent is set to pll_ref */
|
||||
val = pll_readl_base(pll);
|
||||
val_aux = pll_readl(pll->params->aux_reg, pll);
|
||||
|
||||
if (val & PLL_BASE_ENABLE) {
|
||||
if ((val_aux & PLLE_AUX_PLLRE_SEL) ||
|
||||
(val_aux & PLLE_AUX_PLLP_SEL))
|
||||
WARN(1, "pll_e enabled with unsupported parent %s\n",
|
||||
(val_aux & PLLE_AUX_PLLP_SEL) ? "pllp_out0" :
|
||||
"pll_re_vco");
|
||||
} else {
|
||||
val_aux &= ~(PLLE_AUX_PLLRE_SEL | PLLE_AUX_PLLP_SEL);
|
||||
pll_writel(val_aux, pll->params->aux_reg, pll);
|
||||
fence_udelay(1, pll->clk_base);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct tegra_clk_pll *_tegra_init_pll(void __iomem *clk_base,
|
||||
|
@ -2214,27 +2257,12 @@ struct clk *tegra_clk_register_plle_tegra114(const char *name,
|
|||
{
|
||||
struct tegra_clk_pll *pll;
|
||||
struct clk *clk;
|
||||
u32 val, val_aux;
|
||||
|
||||
pll = _tegra_init_pll(clk_base, NULL, pll_params, lock);
|
||||
if (IS_ERR(pll))
|
||||
return ERR_CAST(pll);
|
||||
|
||||
/* ensure parent is set to pll_re_vco */
|
||||
|
||||
val = pll_readl_base(pll);
|
||||
val_aux = pll_readl(pll_params->aux_reg, pll);
|
||||
|
||||
if (val & PLL_BASE_ENABLE) {
|
||||
if ((val_aux & PLLE_AUX_PLLRE_SEL) ||
|
||||
(val_aux & PLLE_AUX_PLLP_SEL))
|
||||
WARN(1, "pll_e enabled with unsupported parent %s\n",
|
||||
(val_aux & PLLE_AUX_PLLP_SEL) ? "pllp_out0" :
|
||||
"pll_re_vco");
|
||||
} else {
|
||||
val_aux &= ~(PLLE_AUX_PLLRE_SEL | PLLE_AUX_PLLP_SEL);
|
||||
pll_writel(val_aux, pll_params->aux_reg, pll);
|
||||
}
|
||||
_clk_plle_tegra_init_parent(pll);
|
||||
|
||||
clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
|
||||
&tegra_clk_plle_tegra114_ops);
|
||||
|
@ -2276,6 +2304,7 @@ static const struct clk_ops tegra_clk_pllss_ops = {
|
|||
.recalc_rate = clk_pll_recalc_rate,
|
||||
.round_rate = clk_pll_ramp_round_rate,
|
||||
.set_rate = clk_pllxc_set_rate,
|
||||
.restore_context = tegra_clk_pll_restore_context,
|
||||
};
|
||||
|
||||
struct clk *tegra_clk_register_pllss(const char *name, const char *parent_name,
|
||||
|
@ -2520,11 +2549,19 @@ out:
|
|||
spin_unlock_irqrestore(pll->lock, flags);
|
||||
}
|
||||
|
||||
static void tegra_clk_plle_t210_restore_context(struct clk_hw *hw)
|
||||
{
|
||||
struct tegra_clk_pll *pll = to_clk_pll(hw);
|
||||
|
||||
_clk_plle_tegra_init_parent(pll);
|
||||
}
|
||||
|
||||
static const struct clk_ops tegra_clk_plle_tegra210_ops = {
|
||||
.is_enabled = clk_plle_tegra210_is_enabled,
|
||||
.enable = clk_plle_tegra210_enable,
|
||||
.disable = clk_plle_tegra210_disable,
|
||||
.recalc_rate = clk_pll_recalc_rate,
|
||||
.restore_context = tegra_clk_plle_t210_restore_context,
|
||||
};
|
||||
|
||||
struct clk *tegra_clk_register_plle_tegra210(const char *name,
|
||||
|
@ -2535,27 +2572,12 @@ struct clk *tegra_clk_register_plle_tegra210(const char *name,
|
|||
{
|
||||
struct tegra_clk_pll *pll;
|
||||
struct clk *clk;
|
||||
u32 val, val_aux;
|
||||
|
||||
pll = _tegra_init_pll(clk_base, NULL, pll_params, lock);
|
||||
if (IS_ERR(pll))
|
||||
return ERR_CAST(pll);
|
||||
|
||||
/* ensure parent is set to pll_re_vco */
|
||||
|
||||
val = pll_readl_base(pll);
|
||||
val_aux = pll_readl(pll_params->aux_reg, pll);
|
||||
|
||||
if (val & PLLE_BASE_ENABLE) {
|
||||
if ((val_aux & PLLE_AUX_PLLRE_SEL) ||
|
||||
(val_aux & PLLE_AUX_PLLP_SEL))
|
||||
WARN(1, "pll_e enabled with unsupported parent %s\n",
|
||||
(val_aux & PLLE_AUX_PLLP_SEL) ? "pllp_out0" :
|
||||
"pll_re_vco");
|
||||
} else {
|
||||
val_aux &= ~(PLLE_AUX_PLLRE_SEL | PLLE_AUX_PLLP_SEL);
|
||||
pll_writel(val_aux, pll_params->aux_reg, pll);
|
||||
}
|
||||
_clk_plle_tegra_init_parent(pll);
|
||||
|
||||
clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
|
||||
&tegra_clk_plle_tegra210_ops);
|
||||
|
|
|
@ -194,6 +194,21 @@ static void clk_sdmmc_mux_disable(struct clk_hw *hw)
|
|||
gate_ops->disable(gate_hw);
|
||||
}
|
||||
|
||||
static void clk_sdmmc_mux_restore_context(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_hw *parent = clk_hw_get_parent(hw);
|
||||
unsigned long parent_rate = clk_hw_get_rate(parent);
|
||||
unsigned long rate = clk_hw_get_rate(hw);
|
||||
int parent_id;
|
||||
|
||||
parent_id = clk_hw_get_parent_index(hw);
|
||||
if (WARN_ON(parent_id < 0))
|
||||
return;
|
||||
|
||||
clk_sdmmc_mux_set_parent(hw, parent_id);
|
||||
clk_sdmmc_mux_set_rate(hw, rate, parent_rate);
|
||||
}
|
||||
|
||||
static const struct clk_ops tegra_clk_sdmmc_mux_ops = {
|
||||
.get_parent = clk_sdmmc_mux_get_parent,
|
||||
.set_parent = clk_sdmmc_mux_set_parent,
|
||||
|
@ -203,6 +218,7 @@ static const struct clk_ops tegra_clk_sdmmc_mux_ops = {
|
|||
.is_enabled = clk_sdmmc_mux_is_enabled,
|
||||
.enable = clk_sdmmc_mux_enable,
|
||||
.disable = clk_sdmmc_mux_disable,
|
||||
.restore_context = clk_sdmmc_mux_restore_context,
|
||||
};
|
||||
|
||||
struct clk *tegra_clk_register_sdmmc_mux_div(const char *name,
|
||||
|
|
|
@ -28,6 +28,9 @@
|
|||
#define super_state_to_src_shift(m, s) ((m->width * s))
|
||||
#define super_state_to_src_mask(m) (((1 << m->width) - 1))
|
||||
|
||||
#define CCLK_SRC_PLLP_OUT0 4
|
||||
#define CCLK_SRC_PLLP_OUT4 5
|
||||
|
||||
static u8 clk_super_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct tegra_clk_super_mux *mux = to_clk_super_mux(hw);
|
||||
|
@ -97,12 +100,23 @@ static int clk_super_set_parent(struct clk_hw *hw, u8 index)
|
|||
if (index == mux->div2_index)
|
||||
index = mux->pllx_index;
|
||||
}
|
||||
|
||||
/* enable PLLP branches to CPU before selecting PLLP source */
|
||||
if ((mux->flags & TEGRA210_CPU_CLK) &&
|
||||
(index == CCLK_SRC_PLLP_OUT0 || index == CCLK_SRC_PLLP_OUT4))
|
||||
tegra_clk_set_pllp_out_cpu(true);
|
||||
|
||||
val &= ~((super_state_to_src_mask(mux)) << shift);
|
||||
val |= (index & (super_state_to_src_mask(mux))) << shift;
|
||||
|
||||
writel_relaxed(val, mux->reg);
|
||||
udelay(2);
|
||||
|
||||
/* disable PLLP branches to CPU if not used */
|
||||
if ((mux->flags & TEGRA210_CPU_CLK) &&
|
||||
index != CCLK_SRC_PLLP_OUT0 && index != CCLK_SRC_PLLP_OUT4)
|
||||
tegra_clk_set_pllp_out_cpu(false);
|
||||
|
||||
out:
|
||||
if (mux->lock)
|
||||
spin_unlock_irqrestore(mux->lock, flags);
|
||||
|
@ -110,9 +124,21 @@ out:
|
|||
return err;
|
||||
}
|
||||
|
||||
static void clk_super_mux_restore_context(struct clk_hw *hw)
|
||||
{
|
||||
int parent_id;
|
||||
|
||||
parent_id = clk_hw_get_parent_index(hw);
|
||||
if (WARN_ON(parent_id < 0))
|
||||
return;
|
||||
|
||||
clk_super_set_parent(hw, parent_id);
|
||||
}
|
||||
|
||||
static const struct clk_ops tegra_clk_super_mux_ops = {
|
||||
.get_parent = clk_super_get_parent,
|
||||
.set_parent = clk_super_set_parent,
|
||||
.restore_context = clk_super_mux_restore_context,
|
||||
};
|
||||
|
||||
static long clk_super_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
|
@ -148,12 +174,27 @@ static int clk_super_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
return super->div_ops->set_rate(div_hw, rate, parent_rate);
|
||||
}
|
||||
|
||||
static void clk_super_restore_context(struct clk_hw *hw)
|
||||
{
|
||||
struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
|
||||
struct clk_hw *div_hw = &super->frac_div.hw;
|
||||
int parent_id;
|
||||
|
||||
parent_id = clk_hw_get_parent_index(hw);
|
||||
if (WARN_ON(parent_id < 0))
|
||||
return;
|
||||
|
||||
super->div_ops->restore_context(div_hw);
|
||||
clk_super_set_parent(hw, parent_id);
|
||||
}
|
||||
|
||||
const struct clk_ops tegra_clk_super_ops = {
|
||||
.get_parent = clk_super_get_parent,
|
||||
.set_parent = clk_super_set_parent,
|
||||
.set_rate = clk_super_set_rate,
|
||||
.round_rate = clk_super_round_rate,
|
||||
.recalc_rate = clk_super_recalc_rate,
|
||||
.restore_context = clk_super_restore_context,
|
||||
};
|
||||
|
||||
struct clk *tegra_clk_register_super_mux(const char *name,
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
#define OSC_CTRL 0x50
|
||||
#define OSC_CTRL_OSC_FREQ_SHIFT 28
|
||||
#define OSC_CTRL_PLL_REF_DIV_SHIFT 26
|
||||
#define OSC_CTRL_MASK (0x3f2 | \
|
||||
(0xf << OSC_CTRL_OSC_FREQ_SHIFT))
|
||||
|
||||
static u32 osc_ctrl_ctx;
|
||||
|
||||
int __init tegra_osc_clk_init(void __iomem *clk_base, struct tegra_clk *clks,
|
||||
unsigned long *input_freqs, unsigned int num,
|
||||
|
@ -29,6 +33,7 @@ int __init tegra_osc_clk_init(void __iomem *clk_base, struct tegra_clk *clks,
|
|||
unsigned osc_idx;
|
||||
|
||||
val = readl_relaxed(clk_base + OSC_CTRL);
|
||||
osc_ctrl_ctx = val & OSC_CTRL_MASK;
|
||||
osc_idx = val >> OSC_CTRL_OSC_FREQ_SHIFT;
|
||||
|
||||
if (osc_idx < num)
|
||||
|
@ -96,3 +101,13 @@ void __init tegra_fixed_clk_init(struct tegra_clk *tegra_clks)
|
|||
*dt_clk = clk;
|
||||
}
|
||||
}
|
||||
|
||||
void tegra_clk_osc_resume(void __iomem *clk_base)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl_relaxed(clk_base + OSC_CTRL) & ~OSC_CTRL_MASK;
|
||||
val |= osc_ctrl_ctx;
|
||||
writel_relaxed(val, clk_base + OSC_CTRL);
|
||||
fence_udelay(2, clk_base);
|
||||
}
|
||||
|
|
|
@ -262,7 +262,6 @@
|
|||
static DEFINE_SPINLOCK(PLLP_OUTA_lock);
|
||||
static DEFINE_SPINLOCK(PLLP_OUTB_lock);
|
||||
static DEFINE_SPINLOCK(PLLP_OUTC_lock);
|
||||
static DEFINE_SPINLOCK(sor0_lock);
|
||||
|
||||
#define MUX_I2S_SPDIF(_id) \
|
||||
static const char *mux_pllaout0_##_id##_2x_pllp_clkm[] = { "pll_a_out0", \
|
||||
|
@ -587,11 +586,6 @@ static u32 mux_pllp_pllre_clkm_idx[] = {
|
|||
[0] = 0, [1] = 2, [2] = 3,
|
||||
};
|
||||
|
||||
static const char *mux_clkm_plldp_sor0lvds[] = {
|
||||
"clk_m", "pll_dp", "sor0_lvds",
|
||||
};
|
||||
#define mux_clkm_plldp_sor0lvds_idx NULL
|
||||
|
||||
static const char * const mux_dmic1[] = {
|
||||
"pll_a_out0", "dmic1_sync_clk", "pll_p", "clk_m"
|
||||
};
|
||||
|
@ -731,14 +725,12 @@ static struct tegra_periph_init_data periph_clks[] = {
|
|||
MUX8("hdmi_audio", mux_pllp3_pllc_clkm, CLK_SOURCE_HDMI_AUDIO, 176, TEGRA_PERIPH_NO_RESET, tegra_clk_hdmi_audio),
|
||||
MUX8("clk72mhz", mux_pllp3_pllc_clkm, CLK_SOURCE_CLK72MHZ, 177, TEGRA_PERIPH_NO_RESET, tegra_clk_clk72Mhz),
|
||||
MUX8("clk72mhz", mux_pllp_out3_pllp_pllc_clkm, CLK_SOURCE_CLK72MHZ, 177, TEGRA_PERIPH_NO_RESET, tegra_clk_clk72Mhz_8),
|
||||
MUX8_NOGATE_LOCK("sor0_lvds", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_SOR0, tegra_clk_sor0_lvds, &sor0_lock),
|
||||
MUX_FLAGS("csite", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_CSITE, 73, TEGRA_PERIPH_ON_APB, tegra_clk_csite, CLK_IGNORE_UNUSED),
|
||||
MUX_FLAGS("csite", mux_pllp_pllre_clkm, CLK_SOURCE_CSITE, 73, TEGRA_PERIPH_ON_APB, tegra_clk_csite_8, CLK_IGNORE_UNUSED),
|
||||
NODIV("disp1", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_DISP1, 29, 7, 27, 0, tegra_clk_disp1, NULL),
|
||||
NODIV("disp1", mux_pllp_plld_plld2_clkm, CLK_SOURCE_DISP1, 29, 7, 27, 0, tegra_clk_disp1_8, NULL),
|
||||
NODIV("disp2", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_DISP2, 29, 7, 26, 0, tegra_clk_disp2, NULL),
|
||||
NODIV("disp2", mux_pllp_plld_plld2_clkm, CLK_SOURCE_DISP2, 29, 7, 26, 0, tegra_clk_disp2_8, NULL),
|
||||
NODIV("sor0", mux_clkm_plldp_sor0lvds, CLK_SOURCE_SOR0, 14, 3, 182, 0, tegra_clk_sor0, &sor0_lock),
|
||||
UART("uarta", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTA, 6, tegra_clk_uarta),
|
||||
UART("uartb", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTB, 7, tegra_clk_uartb),
|
||||
UART("uartc", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTC, 55, tegra_clk_uartc),
|
||||
|
|
|
@ -180,7 +180,7 @@ static void __init tegra_super_clk_init(void __iomem *clk_base,
|
|||
gen_info->num_cclk_g_parents,
|
||||
CLK_SET_RATE_PARENT,
|
||||
clk_base + CCLKG_BURST_POLICY,
|
||||
0, 4, 8, 0, NULL);
|
||||
TEGRA210_CPU_CLK, 4, 8, 0, NULL);
|
||||
} else {
|
||||
clk = tegra_clk_register_super_mux("cclk_g",
|
||||
gen_info->cclk_g_parents,
|
||||
|
@ -196,6 +196,11 @@ static void __init tegra_super_clk_init(void __iomem *clk_base,
|
|||
dt_clk = tegra_lookup_dt_id(tegra_clk_cclk_lp, tegra_clks);
|
||||
if (dt_clk) {
|
||||
if (gen_info->gen == gen5) {
|
||||
/*
|
||||
* TEGRA210_CPU_CLK flag is not needed for cclk_lp as
|
||||
* cluster switching is not currently supported on
|
||||
* Tegra210 and also cpu_lp is not used.
|
||||
*/
|
||||
clk = tegra_clk_register_super_mux("cclk_lp",
|
||||
gen_info->cclk_lp_parents,
|
||||
gen_info->num_cclk_lp_parents,
|
||||
|
|
|
@ -631,6 +631,7 @@ static int tegra124_dfll_fcpu_remove(struct platform_device *pdev)
|
|||
static const struct dev_pm_ops tegra124_dfll_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(tegra_dfll_runtime_suspend,
|
||||
tegra_dfll_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(tegra_dfll_suspend, tegra_dfll_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver tegra124_dfll_fcpu_driver = {
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#define CLK_SOURCE_CSITE 0x1d4
|
||||
#define CLK_SOURCE_EMC 0x19c
|
||||
#define CLK_SOURCE_SOR0 0x414
|
||||
|
||||
#define RST_DFLL_DVCO 0x2f4
|
||||
#define DVFS_DFLL_RESET_SHIFT 0
|
||||
|
@ -91,6 +92,22 @@
|
|||
/* Tegra CPU clock and reset control regs */
|
||||
#define CLK_RST_CONTROLLER_CPU_CMPLX_STATUS 0x470
|
||||
|
||||
#define MASK(x) (BIT(x) - 1)
|
||||
|
||||
#define MUX8_NOGATE_LOCK(_name, _parents, _offset, _clk_id, _lock) \
|
||||
TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset, \
|
||||
29, MASK(3), 0, 0, 8, 1, TEGRA_DIVIDER_ROUND_UP,\
|
||||
0, TEGRA_PERIPH_NO_GATE, _clk_id,\
|
||||
_parents##_idx, 0, _lock)
|
||||
|
||||
#define NODIV(_name, _parents, _offset, \
|
||||
_mux_shift, _mux_mask, _clk_num, \
|
||||
_gate_flags, _clk_id, _lock) \
|
||||
TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset,\
|
||||
_mux_shift, _mux_mask, 0, 0, 0, 0, 0,\
|
||||
_clk_num, (_gate_flags) | TEGRA_PERIPH_NO_DIV,\
|
||||
_clk_id, _parents##_idx, 0, _lock)
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static struct cpu_clk_suspend_context {
|
||||
u32 clk_csite_src;
|
||||
|
@ -110,6 +127,7 @@ static DEFINE_SPINLOCK(pll_e_lock);
|
|||
static DEFINE_SPINLOCK(pll_re_lock);
|
||||
static DEFINE_SPINLOCK(pll_u_lock);
|
||||
static DEFINE_SPINLOCK(emc_lock);
|
||||
static DEFINE_SPINLOCK(sor0_lock);
|
||||
|
||||
/* possible OSC frequencies in Hz */
|
||||
static unsigned long tegra124_input_freq[] = {
|
||||
|
@ -829,7 +847,7 @@ static struct tegra_clk tegra124_clks[tegra_clk_max] __initdata = {
|
|||
[tegra_clk_adx1] = { .dt_id = TEGRA124_CLK_ADX1, .present = true },
|
||||
[tegra_clk_dpaux] = { .dt_id = TEGRA124_CLK_DPAUX, .present = true },
|
||||
[tegra_clk_sor0] = { .dt_id = TEGRA124_CLK_SOR0, .present = true },
|
||||
[tegra_clk_sor0_lvds] = { .dt_id = TEGRA124_CLK_SOR0_LVDS, .present = true },
|
||||
[tegra_clk_sor0_out] = { .dt_id = TEGRA124_CLK_SOR0_OUT, .present = true },
|
||||
[tegra_clk_gpu] = { .dt_id = TEGRA124_CLK_GPU, .present = true },
|
||||
[tegra_clk_amx1] = { .dt_id = TEGRA124_CLK_AMX1, .present = true },
|
||||
[tegra_clk_uartb] = { .dt_id = TEGRA124_CLK_UARTB, .present = true },
|
||||
|
@ -987,12 +1005,33 @@ static struct tegra_devclk devclks[] __initdata = {
|
|||
{ .con_id = "hda2hdmi", .dt_id = TEGRA124_CLK_HDA2HDMI },
|
||||
};
|
||||
|
||||
static const char * const sor0_parents[] = {
|
||||
"pll_p_out0", "pll_m_out0", "pll_d_out0", "pll_a_out0", "pll_c_out0",
|
||||
"pll_d2_out0", "clk_m",
|
||||
};
|
||||
|
||||
static const char * const sor0_out_parents[] = {
|
||||
"clk_m", "sor0_pad_clkout",
|
||||
};
|
||||
|
||||
static struct tegra_periph_init_data tegra124_periph[] = {
|
||||
TEGRA_INIT_DATA_TABLE("sor0", NULL, NULL, sor0_parents,
|
||||
CLK_SOURCE_SOR0, 29, 0x7, 0, 0, 0, 0,
|
||||
0, 182, 0, tegra_clk_sor0, NULL, 0,
|
||||
&sor0_lock),
|
||||
TEGRA_INIT_DATA_TABLE("sor0_out", NULL, NULL, sor0_out_parents,
|
||||
CLK_SOURCE_SOR0, 14, 0x1, 0, 0, 0, 0,
|
||||
0, 0, TEGRA_PERIPH_NO_GATE, tegra_clk_sor0_out,
|
||||
NULL, 0, &sor0_lock),
|
||||
};
|
||||
|
||||
static struct clk **clks;
|
||||
|
||||
static __init void tegra124_periph_clk_init(void __iomem *clk_base,
|
||||
void __iomem *pmc_base)
|
||||
{
|
||||
struct clk *clk;
|
||||
unsigned int i;
|
||||
|
||||
/* xusb_ss_div2 */
|
||||
clk = clk_register_fixed_factor(NULL, "xusb_ss_div2", "xusb_ss_src", 0,
|
||||
|
@ -1033,6 +1072,20 @@ static __init void tegra124_periph_clk_init(void __iomem *clk_base,
|
|||
clk_register_clkdev(clk, "cml1", NULL);
|
||||
clks[TEGRA124_CLK_CML1] = clk;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tegra124_periph); i++) {
|
||||
struct tegra_periph_init_data *init = &tegra124_periph[i];
|
||||
struct clk **clkp;
|
||||
|
||||
clkp = tegra_lookup_dt_id(init->clk_id, tegra124_clks);
|
||||
if (!clkp) {
|
||||
pr_warn("clock %u not found\n", init->clk_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
clk = tegra_clk_register_periph_data(clk_base, init);
|
||||
*clkp = clk;
|
||||
}
|
||||
|
||||
tegra_periph_clk_init(clk_base, pmc_base, tegra124_clks, &pll_p_params);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,293 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Based on drivers/clk/tegra/clk-emc.c
|
||||
* Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Author: Dmitry Osipenko <digetx@gmail.com>
|
||||
* Copyright (C) 2019 GRATE-DRIVER project
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "tegra-emc-clk: " fmt
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clk/tegra.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "clk.h"
|
||||
|
||||
#define CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK GENMASK(7, 0)
|
||||
#define CLK_SOURCE_EMC_2X_CLK_SRC_MASK GENMASK(31, 30)
|
||||
#define CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT 30
|
||||
|
||||
#define MC_EMC_SAME_FREQ BIT(16)
|
||||
#define USE_PLLM_UD BIT(29)
|
||||
|
||||
#define EMC_SRC_PLL_M 0
|
||||
#define EMC_SRC_PLL_C 1
|
||||
#define EMC_SRC_PLL_P 2
|
||||
#define EMC_SRC_CLK_M 3
|
||||
|
||||
static const char * const emc_parent_clk_names[] = {
|
||||
"pll_m", "pll_c", "pll_p", "clk_m",
|
||||
};
|
||||
|
||||
struct tegra_clk_emc {
|
||||
struct clk_hw hw;
|
||||
void __iomem *reg;
|
||||
bool mc_same_freq;
|
||||
bool want_low_jitter;
|
||||
|
||||
tegra20_clk_emc_round_cb *round_cb;
|
||||
void *cb_arg;
|
||||
};
|
||||
|
||||
static inline struct tegra_clk_emc *to_tegra_clk_emc(struct clk_hw *hw)
|
||||
{
|
||||
return container_of(hw, struct tegra_clk_emc, hw);
|
||||
}
|
||||
|
||||
static unsigned long emc_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
|
||||
u32 val, div;
|
||||
|
||||
val = readl_relaxed(emc->reg);
|
||||
div = val & CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
|
||||
|
||||
return DIV_ROUND_UP(parent_rate * 2, div + 2);
|
||||
}
|
||||
|
||||
static u8 emc_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
|
||||
|
||||
return readl_relaxed(emc->reg) >> CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
|
||||
}
|
||||
|
||||
static int emc_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
|
||||
u32 val, div;
|
||||
|
||||
val = readl_relaxed(emc->reg);
|
||||
val &= ~CLK_SOURCE_EMC_2X_CLK_SRC_MASK;
|
||||
val |= index << CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
|
||||
|
||||
div = val & CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
|
||||
|
||||
if (index == EMC_SRC_PLL_M && div == 0 && emc->want_low_jitter)
|
||||
val |= USE_PLLM_UD;
|
||||
else
|
||||
val &= ~USE_PLLM_UD;
|
||||
|
||||
if (emc->mc_same_freq)
|
||||
val |= MC_EMC_SAME_FREQ;
|
||||
else
|
||||
val &= ~MC_EMC_SAME_FREQ;
|
||||
|
||||
writel_relaxed(val, emc->reg);
|
||||
|
||||
fence_udelay(1, emc->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emc_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
|
||||
unsigned int index;
|
||||
u32 val, div;
|
||||
|
||||
div = div_frac_get(rate, parent_rate, 8, 1, 0);
|
||||
|
||||
val = readl_relaxed(emc->reg);
|
||||
val &= ~CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
|
||||
val |= div;
|
||||
|
||||
index = val >> CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
|
||||
|
||||
if (index == EMC_SRC_PLL_M && div == 0 && emc->want_low_jitter)
|
||||
val |= USE_PLLM_UD;
|
||||
else
|
||||
val &= ~USE_PLLM_UD;
|
||||
|
||||
if (emc->mc_same_freq)
|
||||
val |= MC_EMC_SAME_FREQ;
|
||||
else
|
||||
val &= ~MC_EMC_SAME_FREQ;
|
||||
|
||||
writel_relaxed(val, emc->reg);
|
||||
|
||||
fence_udelay(1, emc->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emc_set_rate_and_parent(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate,
|
||||
u8 index)
|
||||
{
|
||||
struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
|
||||
u32 val, div;
|
||||
|
||||
div = div_frac_get(rate, parent_rate, 8, 1, 0);
|
||||
|
||||
val = readl_relaxed(emc->reg);
|
||||
|
||||
val &= ~CLK_SOURCE_EMC_2X_CLK_SRC_MASK;
|
||||
val |= index << CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
|
||||
|
||||
val &= ~CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
|
||||
val |= div;
|
||||
|
||||
if (index == EMC_SRC_PLL_M && div == 0 && emc->want_low_jitter)
|
||||
val |= USE_PLLM_UD;
|
||||
else
|
||||
val &= ~USE_PLLM_UD;
|
||||
|
||||
if (emc->mc_same_freq)
|
||||
val |= MC_EMC_SAME_FREQ;
|
||||
else
|
||||
val &= ~MC_EMC_SAME_FREQ;
|
||||
|
||||
writel_relaxed(val, emc->reg);
|
||||
|
||||
fence_udelay(1, emc->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emc_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
|
||||
{
|
||||
struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
|
||||
struct clk_hw *parent_hw;
|
||||
unsigned long divided_rate;
|
||||
unsigned long parent_rate;
|
||||
unsigned int i;
|
||||
long emc_rate;
|
||||
int div;
|
||||
|
||||
emc_rate = emc->round_cb(req->rate, req->min_rate, req->max_rate,
|
||||
emc->cb_arg);
|
||||
if (emc_rate < 0)
|
||||
return emc_rate;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(emc_parent_clk_names); i++) {
|
||||
parent_hw = clk_hw_get_parent_by_index(hw, i);
|
||||
|
||||
if (req->best_parent_hw == parent_hw)
|
||||
parent_rate = req->best_parent_rate;
|
||||
else
|
||||
parent_rate = clk_hw_get_rate(parent_hw);
|
||||
|
||||
if (emc_rate > parent_rate)
|
||||
continue;
|
||||
|
||||
div = div_frac_get(emc_rate, parent_rate, 8, 1, 0);
|
||||
divided_rate = DIV_ROUND_UP(parent_rate * 2, div + 2);
|
||||
|
||||
if (divided_rate != emc_rate)
|
||||
continue;
|
||||
|
||||
req->best_parent_rate = parent_rate;
|
||||
req->best_parent_hw = parent_hw;
|
||||
req->rate = emc_rate;
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(emc_parent_clk_names)) {
|
||||
pr_err_once("can't find parent for rate %lu emc_rate %lu\n",
|
||||
req->rate, emc_rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops tegra_clk_emc_ops = {
|
||||
.recalc_rate = emc_recalc_rate,
|
||||
.get_parent = emc_get_parent,
|
||||
.set_parent = emc_set_parent,
|
||||
.set_rate = emc_set_rate,
|
||||
.set_rate_and_parent = emc_set_rate_and_parent,
|
||||
.determine_rate = emc_determine_rate,
|
||||
};
|
||||
|
||||
void tegra20_clk_set_emc_round_callback(tegra20_clk_emc_round_cb *round_cb,
|
||||
void *cb_arg)
|
||||
{
|
||||
struct clk *clk = __clk_lookup("emc");
|
||||
struct tegra_clk_emc *emc;
|
||||
struct clk_hw *hw;
|
||||
|
||||
if (clk) {
|
||||
hw = __clk_get_hw(clk);
|
||||
emc = to_tegra_clk_emc(hw);
|
||||
|
||||
emc->round_cb = round_cb;
|
||||
emc->cb_arg = cb_arg;
|
||||
}
|
||||
}
|
||||
|
||||
bool tegra20_clk_emc_driver_available(struct clk_hw *emc_hw)
|
||||
{
|
||||
return to_tegra_clk_emc(emc_hw)->round_cb != NULL;
|
||||
}
|
||||
|
||||
struct clk *tegra20_clk_register_emc(void __iomem *ioaddr, bool low_jitter)
|
||||
{
|
||||
struct tegra_clk_emc *emc;
|
||||
struct clk_init_data init;
|
||||
struct clk *clk;
|
||||
|
||||
emc = kzalloc(sizeof(*emc), GFP_KERNEL);
|
||||
if (!emc)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* EMC stands for External Memory Controller.
|
||||
*
|
||||
* We don't want EMC clock to be disabled ever by gating its
|
||||
* parent and whatnot because system is busted immediately in that
|
||||
* case, hence the clock is marked as critical.
|
||||
*/
|
||||
init.name = "emc";
|
||||
init.ops = &tegra_clk_emc_ops;
|
||||
init.flags = CLK_IS_CRITICAL;
|
||||
init.parent_names = emc_parent_clk_names;
|
||||
init.num_parents = ARRAY_SIZE(emc_parent_clk_names);
|
||||
|
||||
emc->reg = ioaddr;
|
||||
emc->hw.init = &init;
|
||||
emc->want_low_jitter = low_jitter;
|
||||
|
||||
clk = clk_register(NULL, &emc->hw);
|
||||
if (IS_ERR(clk)) {
|
||||
kfree(emc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
int tegra20_clk_prepare_emc_mc_same_freq(struct clk *emc_clk, bool same)
|
||||
{
|
||||
struct tegra_clk_emc *emc;
|
||||
struct clk_hw *hw;
|
||||
|
||||
if (!emc_clk)
|
||||
return -EINVAL;
|
||||
|
||||
hw = __clk_get_hw(emc_clk);
|
||||
emc = to_tegra_clk_emc(hw);
|
||||
emc->mc_same_freq = same;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -130,8 +130,6 @@ static struct cpu_clk_suspend_context {
|
|||
static void __iomem *clk_base;
|
||||
static void __iomem *pmc_base;
|
||||
|
||||
static DEFINE_SPINLOCK(emc_lock);
|
||||
|
||||
#define TEGRA_INIT_DATA_MUX(_name, _parents, _offset, \
|
||||
_clk_num, _gate_flags, _clk_id) \
|
||||
TEGRA_INIT_DATA(_name, NULL, NULL, _parents, _offset, \
|
||||
|
@ -760,7 +758,6 @@ static const char *pwm_parents[] = { "pll_p", "pll_c", "audio", "clk_m",
|
|||
static const char *mux_pllpcm_clkm[] = { "pll_p", "pll_c", "pll_m", "clk_m" };
|
||||
static const char *mux_pllpdc_clkm[] = { "pll_p", "pll_d_out0", "pll_c",
|
||||
"clk_m" };
|
||||
static const char *mux_pllmcp_clkm[] = { "pll_m", "pll_c", "pll_p", "clk_m" };
|
||||
|
||||
static struct tegra_periph_init_data tegra_periph_clk_list[] = {
|
||||
TEGRA_INIT_DATA_MUX("i2s1", i2s1_parents, CLK_SOURCE_I2S1, 11, TEGRA_PERIPH_ON_APB, TEGRA20_CLK_I2S1),
|
||||
|
@ -787,41 +784,6 @@ static struct tegra_periph_init_data tegra_periph_nodiv_clk_list[] = {
|
|||
TEGRA_INIT_DATA_NODIV("disp2", mux_pllpdc_clkm, CLK_SOURCE_DISP2, 30, 2, 26, 0, TEGRA20_CLK_DISP2),
|
||||
};
|
||||
|
||||
static void __init tegra20_emc_clk_init(void)
|
||||
{
|
||||
const u32 use_pllm_ud = BIT(29);
|
||||
struct clk *clk;
|
||||
u32 emc_reg;
|
||||
|
||||
clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm,
|
||||
ARRAY_SIZE(mux_pllmcp_clkm),
|
||||
CLK_SET_RATE_NO_REPARENT,
|
||||
clk_base + CLK_SOURCE_EMC,
|
||||
30, 2, 0, &emc_lock);
|
||||
|
||||
clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC,
|
||||
&emc_lock);
|
||||
clks[TEGRA20_CLK_MC] = clk;
|
||||
|
||||
/* un-divided pll_m_out0 is currently unsupported */
|
||||
emc_reg = readl_relaxed(clk_base + CLK_SOURCE_EMC);
|
||||
if (emc_reg & use_pllm_ud) {
|
||||
pr_err("%s: un-divided PllM_out0 used as clock source\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that 'emc_mux' source and 'emc' rate shouldn't be changed at
|
||||
* the same time due to a HW bug, this won't happen because we're
|
||||
* defining 'emc_mux' and 'emc' as distinct clocks.
|
||||
*/
|
||||
clk = tegra_clk_register_divider("emc", "emc_mux",
|
||||
clk_base + CLK_SOURCE_EMC, CLK_IS_CRITICAL,
|
||||
TEGRA_DIVIDER_INT, 0, 8, 1, &emc_lock);
|
||||
clks[TEGRA20_CLK_EMC] = clk;
|
||||
}
|
||||
|
||||
static void __init tegra20_periph_clk_init(void)
|
||||
{
|
||||
struct tegra_periph_init_data *data;
|
||||
|
@ -835,7 +797,13 @@ static void __init tegra20_periph_clk_init(void)
|
|||
clks[TEGRA20_CLK_AC97] = clk;
|
||||
|
||||
/* emc */
|
||||
tegra20_emc_clk_init();
|
||||
clk = tegra20_clk_register_emc(clk_base + CLK_SOURCE_EMC, false);
|
||||
|
||||
clks[TEGRA20_CLK_EMC] = clk;
|
||||
|
||||
clk = tegra_clk_register_mc("mc", "emc", clk_base + CLK_SOURCE_EMC,
|
||||
NULL);
|
||||
clks[TEGRA20_CLK_MC] = clk;
|
||||
|
||||
/* dsi */
|
||||
clk = tegra_clk_register_periph_gate("dsi", "pll_d", 0, clk_base, 0,
|
||||
|
@ -987,6 +955,7 @@ static void tegra20_cpu_clock_suspend(void)
|
|||
static void tegra20_cpu_clock_resume(void)
|
||||
{
|
||||
unsigned int reg, policy;
|
||||
u32 misc, base;
|
||||
|
||||
/* Is CPU complex already running on PLLX? */
|
||||
reg = readl(clk_base + CCLK_BURST_POLICY);
|
||||
|
@ -1000,15 +969,21 @@ static void tegra20_cpu_clock_resume(void)
|
|||
BUG();
|
||||
|
||||
if (reg != CCLK_BURST_POLICY_PLLX) {
|
||||
/* restore PLLX settings if CPU is on different PLL */
|
||||
writel(tegra20_cpu_clk_sctx.pllx_misc,
|
||||
clk_base + PLLX_MISC);
|
||||
writel(tegra20_cpu_clk_sctx.pllx_base,
|
||||
clk_base + PLLX_BASE);
|
||||
misc = readl_relaxed(clk_base + PLLX_MISC);
|
||||
base = readl_relaxed(clk_base + PLLX_BASE);
|
||||
|
||||
/* wait for PLL stabilization if PLLX was enabled */
|
||||
if (tegra20_cpu_clk_sctx.pllx_base & (1 << 30))
|
||||
udelay(300);
|
||||
if (misc != tegra20_cpu_clk_sctx.pllx_misc ||
|
||||
base != tegra20_cpu_clk_sctx.pllx_base) {
|
||||
/* restore PLLX settings if CPU is on different PLL */
|
||||
writel(tegra20_cpu_clk_sctx.pllx_misc,
|
||||
clk_base + PLLX_MISC);
|
||||
writel(tegra20_cpu_clk_sctx.pllx_base,
|
||||
clk_base + PLLX_BASE);
|
||||
|
||||
/* wait for PLL stabilization if PLLX was enabled */
|
||||
if (tegra20_cpu_clk_sctx.pllx_base & (1 << 30))
|
||||
udelay(300);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1115,6 +1090,8 @@ static struct clk *tegra20_clk_src_onecell_get(struct of_phandle_args *clkspec,
|
|||
if (IS_ERR(clk))
|
||||
return clk;
|
||||
|
||||
hw = __clk_get_hw(clk);
|
||||
|
||||
/*
|
||||
* Tegra20 CDEV1 and CDEV2 clocks are a bit special case, their parent
|
||||
* clock is created by the pinctrl driver. It is possible for clk user
|
||||
|
@ -1124,13 +1101,16 @@ static struct clk *tegra20_clk_src_onecell_get(struct of_phandle_args *clkspec,
|
|||
*/
|
||||
if (clkspec->args[0] == TEGRA20_CLK_CDEV1 ||
|
||||
clkspec->args[0] == TEGRA20_CLK_CDEV2) {
|
||||
hw = __clk_get_hw(clk);
|
||||
|
||||
parent_hw = clk_hw_get_parent(hw);
|
||||
if (!parent_hw)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
|
||||
if (clkspec->args[0] == TEGRA20_CLK_EMC) {
|
||||
if (!tegra20_clk_emc_driver_available(hw))
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,13 +9,13 @@
|
|||
#include <linux/clkdev.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/clk/tegra.h>
|
||||
#include <dt-bindings/clock/tegra210-car.h>
|
||||
#include <dt-bindings/reset/tegra210-car.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <soc/tegra/pmc.h>
|
||||
|
||||
|
@ -33,6 +33,7 @@
|
|||
#define CLK_SOURCE_CSITE 0x1d4
|
||||
#define CLK_SOURCE_EMC 0x19c
|
||||
#define CLK_SOURCE_SOR1 0x410
|
||||
#define CLK_SOURCE_SOR0 0x414
|
||||
#define CLK_SOURCE_LA 0x1f8
|
||||
#define CLK_SOURCE_SDMMC2 0x154
|
||||
#define CLK_SOURCE_SDMMC4 0x164
|
||||
|
@ -220,11 +221,15 @@
|
|||
#define CLK_M_DIVISOR_SHIFT 2
|
||||
#define CLK_M_DIVISOR_MASK 0x3
|
||||
|
||||
#define CLK_MASK_ARM 0x44
|
||||
#define MISC_CLK_ENB 0x48
|
||||
|
||||
#define RST_DFLL_DVCO 0x2f4
|
||||
#define DVFS_DFLL_RESET_SHIFT 0
|
||||
|
||||
#define CLK_RST_CONTROLLER_RST_DEV_Y_SET 0x2a8
|
||||
#define CLK_RST_CONTROLLER_RST_DEV_Y_CLR 0x2ac
|
||||
#define CPU_SOFTRST_CTRL 0x380
|
||||
|
||||
#define LVL2_CLK_GATE_OVRA 0xf8
|
||||
#define LVL2_CLK_GATE_OVRC 0x3a0
|
||||
|
@ -298,6 +303,7 @@ static DEFINE_SPINLOCK(pll_d_lock);
|
|||
static DEFINE_SPINLOCK(pll_e_lock);
|
||||
static DEFINE_SPINLOCK(pll_re_lock);
|
||||
static DEFINE_SPINLOCK(pll_u_lock);
|
||||
static DEFINE_SPINLOCK(sor0_lock);
|
||||
static DEFINE_SPINLOCK(sor1_lock);
|
||||
static DEFINE_SPINLOCK(emc_lock);
|
||||
static DEFINE_MUTEX(lvl2_ovr_lock);
|
||||
|
@ -2351,9 +2357,9 @@ static struct tegra_clk tegra210_clks[tegra_clk_max] __initdata = {
|
|||
[tegra_clk_dpaux] = { .dt_id = TEGRA210_CLK_DPAUX, .present = true },
|
||||
[tegra_clk_dpaux1] = { .dt_id = TEGRA210_CLK_DPAUX1, .present = true },
|
||||
[tegra_clk_sor0] = { .dt_id = TEGRA210_CLK_SOR0, .present = true },
|
||||
[tegra_clk_sor0_lvds] = { .dt_id = TEGRA210_CLK_SOR0_LVDS, .present = true },
|
||||
[tegra_clk_sor0_out] = { .dt_id = TEGRA210_CLK_SOR0_OUT, .present = true },
|
||||
[tegra_clk_sor1] = { .dt_id = TEGRA210_CLK_SOR1, .present = true },
|
||||
[tegra_clk_sor1_src] = { .dt_id = TEGRA210_CLK_SOR1_SRC, .present = true },
|
||||
[tegra_clk_sor1_out] = { .dt_id = TEGRA210_CLK_SOR1_OUT, .present = true },
|
||||
[tegra_clk_gpu] = { .dt_id = TEGRA210_CLK_GPU, .present = true },
|
||||
[tegra_clk_pll_g_ref] = { .dt_id = TEGRA210_CLK_PLL_G_REF, .present = true, },
|
||||
[tegra_clk_uartb_8] = { .dt_id = TEGRA210_CLK_UARTB, .present = true },
|
||||
|
@ -2551,7 +2557,6 @@ static struct tegra_devclk devclks[] __initdata = {
|
|||
{ .con_id = "pll_c4_out2", .dt_id = TEGRA210_CLK_PLL_C4_OUT2 },
|
||||
{ .con_id = "pll_c4_out3", .dt_id = TEGRA210_CLK_PLL_C4_OUT3 },
|
||||
{ .con_id = "dpaux", .dt_id = TEGRA210_CLK_DPAUX },
|
||||
{ .con_id = "sor0", .dt_id = TEGRA210_CLK_SOR0 },
|
||||
};
|
||||
|
||||
static struct tegra_audio_clk_info tegra210_audio_plls[] = {
|
||||
|
@ -2825,6 +2830,7 @@ static int tegra210_enable_pllu(void)
|
|||
struct tegra_clk_pll_freq_table *fentry;
|
||||
struct tegra_clk_pll pllu;
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
for (fentry = pll_u_freq_table; fentry->input_rate; fentry++) {
|
||||
if (fentry->input_rate == pll_ref_freq)
|
||||
|
@ -2841,7 +2847,7 @@ static int tegra210_enable_pllu(void)
|
|||
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]);
|
||||
udelay(5);
|
||||
fence_udelay(5, clk_base);
|
||||
|
||||
reg = readl_relaxed(clk_base + PLLU_BASE);
|
||||
reg &= ~GENMASK(20, 0);
|
||||
|
@ -2849,13 +2855,18 @@ static int tegra210_enable_pllu(void)
|
|||
reg |= fentry->n << 8;
|
||||
reg |= fentry->p << 16;
|
||||
writel(reg, clk_base + PLLU_BASE);
|
||||
udelay(1);
|
||||
fence_udelay(1, clk_base);
|
||||
reg |= PLL_ENABLE;
|
||||
writel(reg, clk_base + PLLU_BASE);
|
||||
|
||||
readl_relaxed_poll_timeout_atomic(clk_base + PLLU_BASE, reg,
|
||||
reg & PLL_BASE_LOCK, 2, 1000);
|
||||
if (!(reg & PLL_BASE_LOCK)) {
|
||||
/*
|
||||
* During clocks resume, same PLLU init and enable sequence get
|
||||
* executed. So, readx_poll_timeout_atomic can't be used here as it
|
||||
* uses ktime_get() and timekeeping resume doesn't happen by that
|
||||
* time. So, using tegra210_wait_for_mask for PLL LOCK.
|
||||
*/
|
||||
ret = tegra210_wait_for_mask(&pllu, PLLU_BASE, PLL_BASE_LOCK);
|
||||
if (ret) {
|
||||
pr_err("Timed out waiting for PLL_U to lock\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
@ -2895,12 +2906,12 @@ static int tegra210_init_pllu(void)
|
|||
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);
|
||||
fence_udelay(1, clk_base);
|
||||
|
||||
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);
|
||||
fence_udelay(1, clk_base);
|
||||
|
||||
reg = readl_relaxed(clk_base + PLLU_BASE);
|
||||
reg &= ~PLLU_BASE_CLKENABLE_USB;
|
||||
|
@ -2915,15 +2926,31 @@ static int tegra210_init_pllu(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const char * const sor1_out_parents[] = {
|
||||
/*
|
||||
* Bit 0 of the mux selects sor1_pad_clkout, irrespective of bit 1, so
|
||||
* the sor1_pad_clkout parent appears twice in the list below. This is
|
||||
* merely to support clk_get_parent() if firmware happened to set
|
||||
* these bits to 0b11. While not an invalid setting, code should
|
||||
* always set the bits to 0b01 to select sor1_pad_clkout.
|
||||
*/
|
||||
"sor_safe", "sor1_pad_clkout", "sor1", "sor1_pad_clkout",
|
||||
/*
|
||||
* The SOR hardware blocks are driven by two clocks: a module clock that is
|
||||
* used to access registers and a pixel clock that is sourced from the same
|
||||
* pixel clock that also drives the head attached to the SOR. The module
|
||||
* clock is typically called sorX (with X being the SOR instance) and the
|
||||
* pixel clock is called sorX_out. The source for the SOR pixel clock is
|
||||
* referred to as the "parent" clock.
|
||||
*
|
||||
* On Tegra186 and newer, clocks are provided by the BPMP. Unfortunately the
|
||||
* BPMP implementation for the SOR clocks doesn't exactly match the above in
|
||||
* some aspects. For example, the SOR module is really clocked by the pad or
|
||||
* sor_safe clocks, but BPMP models the sorX clock as being sourced by the
|
||||
* pixel clocks. Conversely the sorX_out clock is sourced by the sor_safe or
|
||||
* pad clocks on BPMP.
|
||||
*
|
||||
* In order to allow the display driver to deal with all SoC generations in
|
||||
* a unified way, implement the BPMP semantics in this driver.
|
||||
*/
|
||||
|
||||
static const char * const sor0_parents[] = {
|
||||
"pll_d_out0",
|
||||
};
|
||||
|
||||
static const char * const sor0_out_parents[] = {
|
||||
"sor_safe", "sor0_pad_clkout",
|
||||
};
|
||||
|
||||
static const char * const sor1_parents[] = {
|
||||
|
@ -2932,11 +2959,39 @@ static const char * const sor1_parents[] = {
|
|||
|
||||
static u32 sor1_parents_idx[] = { 0, 2, 5, 6 };
|
||||
|
||||
static const char * const sor1_out_parents[] = {
|
||||
/*
|
||||
* Bit 0 of the mux selects sor1_pad_clkout, irrespective of bit 1, so
|
||||
* the sor1_pad_clkout parent appears twice in the list below. This is
|
||||
* merely to support clk_get_parent() if firmware happened to set
|
||||
* these bits to 0b11. While not an invalid setting, code should
|
||||
* always set the bits to 0b01 to select sor1_pad_clkout.
|
||||
*/
|
||||
"sor_safe", "sor1_pad_clkout", "sor1_out", "sor1_pad_clkout",
|
||||
};
|
||||
|
||||
static struct tegra_periph_init_data tegra210_periph[] = {
|
||||
/*
|
||||
* On Tegra210, the sor0 clock doesn't have a mux it bitfield 31:29,
|
||||
* but it is hardwired to the pll_d_out0 clock.
|
||||
*/
|
||||
TEGRA_INIT_DATA_TABLE("sor0", NULL, NULL, sor0_parents,
|
||||
CLK_SOURCE_SOR0, 29, 0x0, 0, 0, 0, 0,
|
||||
0, 182, 0, tegra_clk_sor0, NULL, 0,
|
||||
&sor0_lock),
|
||||
TEGRA_INIT_DATA_TABLE("sor0_out", NULL, NULL, sor0_out_parents,
|
||||
CLK_SOURCE_SOR0, 14, 0x1, 0, 0, 0, 0,
|
||||
0, 0, TEGRA_PERIPH_NO_GATE, tegra_clk_sor0_out,
|
||||
NULL, 0, &sor0_lock),
|
||||
TEGRA_INIT_DATA_TABLE("sor1", NULL, NULL, sor1_parents,
|
||||
CLK_SOURCE_SOR1, 29, 0x7, 0, 0, 8, 1,
|
||||
TEGRA_DIVIDER_ROUND_UP, 183, 0, tegra_clk_sor1,
|
||||
sor1_parents_idx, 0, &sor1_lock),
|
||||
TEGRA_DIVIDER_ROUND_UP, 183, 0,
|
||||
tegra_clk_sor1, sor1_parents_idx, 0,
|
||||
&sor1_lock),
|
||||
TEGRA_INIT_DATA_TABLE("sor1_out", NULL, NULL, sor1_out_parents,
|
||||
CLK_SOURCE_SOR1, 14, 0x3, 0, 0, 0, 0,
|
||||
0, 0, TEGRA_PERIPH_NO_GATE,
|
||||
tegra_clk_sor1_out, NULL, 0, &sor1_lock),
|
||||
};
|
||||
|
||||
static const char * const la_parents[] = {
|
||||
|
@ -2969,12 +3024,6 @@ static __init void tegra210_periph_clk_init(void __iomem *clk_base,
|
|||
1, 17, 207);
|
||||
clks[TEGRA210_CLK_DPAUX1] = clk;
|
||||
|
||||
clk = clk_register_mux_table(NULL, "sor1_out", sor1_out_parents,
|
||||
ARRAY_SIZE(sor1_out_parents), 0,
|
||||
clk_base + CLK_SOURCE_SOR1, 14, 0x3,
|
||||
0, NULL, &sor1_lock);
|
||||
clks[TEGRA210_CLK_SOR1_OUT] = clk;
|
||||
|
||||
/* pll_d_dsi_out */
|
||||
clk = clk_register_gate(NULL, "pll_d_dsi_out", "pll_d_out0", 0,
|
||||
clk_base + PLLD_MISC0, 21, 0, &pll_d_lock);
|
||||
|
@ -3287,6 +3336,77 @@ static void tegra210_disable_cpu_clock(u32 cpu)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
#define car_readl(_base, _off) readl_relaxed(clk_base + (_base) + ((_off) * 4))
|
||||
#define car_writel(_val, _base, _off) \
|
||||
writel_relaxed(_val, clk_base + (_base) + ((_off) * 4))
|
||||
|
||||
static u32 spare_reg_ctx, misc_clk_enb_ctx, clk_msk_arm_ctx;
|
||||
static u32 cpu_softrst_ctx[3];
|
||||
|
||||
static int tegra210_clk_suspend(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
clk_save_context();
|
||||
|
||||
/*
|
||||
* Save the bootloader configured clock registers SPARE_REG0,
|
||||
* MISC_CLK_ENB, CLK_MASK_ARM, CPU_SOFTRST_CTRL.
|
||||
*/
|
||||
spare_reg_ctx = readl_relaxed(clk_base + SPARE_REG0);
|
||||
misc_clk_enb_ctx = readl_relaxed(clk_base + MISC_CLK_ENB);
|
||||
clk_msk_arm_ctx = readl_relaxed(clk_base + CLK_MASK_ARM);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cpu_softrst_ctx); i++)
|
||||
cpu_softrst_ctx[i] = car_readl(CPU_SOFTRST_CTRL, i);
|
||||
|
||||
tegra_clk_periph_suspend();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tegra210_clk_resume(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
tegra_clk_osc_resume(clk_base);
|
||||
|
||||
/*
|
||||
* Restore the bootloader configured clock registers SPARE_REG0,
|
||||
* MISC_CLK_ENB, CLK_MASK_ARM, CPU_SOFTRST_CTRL from saved context.
|
||||
*/
|
||||
writel_relaxed(spare_reg_ctx, clk_base + SPARE_REG0);
|
||||
writel_relaxed(misc_clk_enb_ctx, clk_base + MISC_CLK_ENB);
|
||||
writel_relaxed(clk_msk_arm_ctx, clk_base + CLK_MASK_ARM);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cpu_softrst_ctx); i++)
|
||||
car_writel(cpu_softrst_ctx[i], CPU_SOFTRST_CTRL, i);
|
||||
|
||||
/*
|
||||
* Tegra clock programming sequence recommends peripheral clock to
|
||||
* be enabled prior to changing its clock source and divider to
|
||||
* prevent glitchless frequency switch.
|
||||
* So, enable all peripheral clocks before restoring their source
|
||||
* and dividers.
|
||||
*/
|
||||
writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_L, clk_base + CLK_OUT_ENB_L);
|
||||
writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_H, clk_base + CLK_OUT_ENB_H);
|
||||
writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_U, clk_base + CLK_OUT_ENB_U);
|
||||
writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_V, clk_base + CLK_OUT_ENB_V);
|
||||
writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_W, clk_base + CLK_OUT_ENB_W);
|
||||
writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_X, clk_base + CLK_OUT_ENB_X);
|
||||
writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_Y, clk_base + CLK_OUT_ENB_Y);
|
||||
|
||||
/* wait for all writes to happen to have all the clocks enabled */
|
||||
fence_udelay(2, clk_base);
|
||||
|
||||
/* restore PLLs and all peripheral clock rates */
|
||||
tegra210_init_pllu();
|
||||
clk_restore_context();
|
||||
|
||||
/* restore saved context of peripheral clocks and reset state */
|
||||
tegra_clk_periph_resume();
|
||||
}
|
||||
|
||||
static void tegra210_cpu_clock_suspend(void)
|
||||
{
|
||||
/* switch coresite to clk_m, save off original source */
|
||||
|
@ -3302,6 +3422,13 @@ static void tegra210_cpu_clock_resume(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
static struct syscore_ops tegra_clk_syscore_ops = {
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.suspend = tegra210_clk_suspend,
|
||||
.resume = tegra210_clk_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct tegra_cpu_car_ops tegra210_cpu_car_ops = {
|
||||
.wait_for_reset = tegra210_wait_cpu_in_reset,
|
||||
.disable_clock = tegra210_disable_cpu_clock,
|
||||
|
@ -3586,5 +3713,7 @@ static void __init tegra210_clock_init(struct device_node *np)
|
|||
tegra210_mbist_clk_init();
|
||||
|
||||
tegra_cpu_car_ops = &tegra210_cpu_car_ops;
|
||||
|
||||
register_syscore_ops(&tegra_clk_syscore_ops);
|
||||
}
|
||||
CLK_OF_DECLARE(tegra210, "nvidia,tegra210-car", tegra210_clock_init);
|
||||
|
|
|
@ -151,7 +151,6 @@ static unsigned long input_freq;
|
|||
|
||||
static DEFINE_SPINLOCK(cml_lock);
|
||||
static DEFINE_SPINLOCK(pll_d_lock);
|
||||
static DEFINE_SPINLOCK(emc_lock);
|
||||
|
||||
#define TEGRA_INIT_DATA_MUX(_name, _parents, _offset, \
|
||||
_clk_num, _gate_flags, _clk_id) \
|
||||
|
@ -808,7 +807,7 @@ static struct tegra_clk tegra30_clks[tegra_clk_max] __initdata = {
|
|||
[tegra_clk_pll_a] = { .dt_id = TEGRA30_CLK_PLL_A, .present = true },
|
||||
[tegra_clk_pll_a_out0] = { .dt_id = TEGRA30_CLK_PLL_A_OUT0, .present = true },
|
||||
[tegra_clk_cec] = { .dt_id = TEGRA30_CLK_CEC, .present = true },
|
||||
[tegra_clk_emc] = { .dt_id = TEGRA30_CLK_EMC, .present = true },
|
||||
[tegra_clk_emc] = { .dt_id = TEGRA30_CLK_EMC, .present = false },
|
||||
};
|
||||
|
||||
static const char *pll_e_parents[] = { "pll_ref", "pll_p" };
|
||||
|
@ -995,7 +994,6 @@ static void __init tegra30_super_clk_init(void)
|
|||
static const char *mux_pllacp_clkm[] = { "pll_a_out0", "unused", "pll_p",
|
||||
"clk_m" };
|
||||
static const char *mux_pllpcm_clkm[] = { "pll_p", "pll_c", "pll_m", "clk_m" };
|
||||
static const char *mux_pllmcp_clkm[] = { "pll_m", "pll_c", "pll_p", "clk_m" };
|
||||
static const char *spdif_out_parents[] = { "pll_a_out0", "spdif_2x", "pll_p",
|
||||
"clk_m" };
|
||||
static const char *mux_pllmcpa[] = { "pll_m", "pll_c", "pll_p", "pll_a_out0" };
|
||||
|
@ -1044,14 +1042,12 @@ static void __init tegra30_periph_clk_init(void)
|
|||
clks[TEGRA30_CLK_AFI] = clk;
|
||||
|
||||
/* emc */
|
||||
clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm,
|
||||
ARRAY_SIZE(mux_pllmcp_clkm),
|
||||
CLK_SET_RATE_NO_REPARENT,
|
||||
clk_base + CLK_SOURCE_EMC,
|
||||
30, 2, 0, &emc_lock);
|
||||
clk = tegra20_clk_register_emc(clk_base + CLK_SOURCE_EMC, true);
|
||||
|
||||
clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC,
|
||||
&emc_lock);
|
||||
clks[TEGRA30_CLK_EMC] = clk;
|
||||
|
||||
clk = tegra_clk_register_mc("mc", "emc", clk_base + CLK_SOURCE_EMC,
|
||||
NULL);
|
||||
clks[TEGRA30_CLK_MC] = clk;
|
||||
|
||||
/* cml0 */
|
||||
|
@ -1167,6 +1163,7 @@ static void tegra30_cpu_clock_suspend(void)
|
|||
static void tegra30_cpu_clock_resume(void)
|
||||
{
|
||||
unsigned int reg, policy;
|
||||
u32 misc, base;
|
||||
|
||||
/* Is CPU complex already running on PLLX? */
|
||||
reg = readl(clk_base + CLK_RESET_CCLK_BURST);
|
||||
|
@ -1180,15 +1177,21 @@ static void tegra30_cpu_clock_resume(void)
|
|||
BUG();
|
||||
|
||||
if (reg != CLK_RESET_CCLK_BURST_POLICY_PLLX) {
|
||||
/* restore PLLX settings if CPU is on different PLL */
|
||||
writel(tegra30_cpu_clk_sctx.pllx_misc,
|
||||
clk_base + CLK_RESET_PLLX_MISC);
|
||||
writel(tegra30_cpu_clk_sctx.pllx_base,
|
||||
clk_base + CLK_RESET_PLLX_BASE);
|
||||
misc = readl_relaxed(clk_base + CLK_RESET_PLLX_MISC);
|
||||
base = readl_relaxed(clk_base + CLK_RESET_PLLX_BASE);
|
||||
|
||||
/* wait for PLL stabilization if PLLX was enabled */
|
||||
if (tegra30_cpu_clk_sctx.pllx_base & (1 << 30))
|
||||
udelay(300);
|
||||
if (misc != tegra30_cpu_clk_sctx.pllx_misc ||
|
||||
base != tegra30_cpu_clk_sctx.pllx_base) {
|
||||
/* restore PLLX settings if CPU is on different PLL */
|
||||
writel(tegra30_cpu_clk_sctx.pllx_misc,
|
||||
clk_base + CLK_RESET_PLLX_MISC);
|
||||
writel(tegra30_cpu_clk_sctx.pllx_base,
|
||||
clk_base + CLK_RESET_PLLX_BASE);
|
||||
|
||||
/* wait for PLL stabilization if PLLX was enabled */
|
||||
if (tegra30_cpu_clk_sctx.pllx_base & (1 << 30))
|
||||
udelay(300);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1302,6 +1305,26 @@ static struct tegra_audio_clk_info tegra30_audio_plls[] = {
|
|||
{ "pll_a", &pll_a_params, tegra_clk_pll_a, "pll_p_out1" },
|
||||
};
|
||||
|
||||
static struct clk *tegra30_clk_src_onecell_get(struct of_phandle_args *clkspec,
|
||||
void *data)
|
||||
{
|
||||
struct clk_hw *hw;
|
||||
struct clk *clk;
|
||||
|
||||
clk = of_clk_src_onecell_get(clkspec, data);
|
||||
if (IS_ERR(clk))
|
||||
return clk;
|
||||
|
||||
hw = __clk_get_hw(clk);
|
||||
|
||||
if (clkspec->args[0] == TEGRA30_CLK_EMC) {
|
||||
if (!tegra20_clk_emc_driver_available(hw))
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
static void __init tegra30_clock_init(struct device_node *np)
|
||||
{
|
||||
struct device_node *node;
|
||||
|
@ -1345,7 +1368,7 @@ static void __init tegra30_clock_init(struct device_node *np)
|
|||
|
||||
tegra_init_dup_clks(tegra_clk_duplicates, clks, TEGRA30_CLK_CLK_MAX);
|
||||
|
||||
tegra_add_of_provider(np, of_clk_src_onecell_get);
|
||||
tegra_add_of_provider(np, tegra30_clk_src_onecell_get);
|
||||
tegra_register_devclks(devclks, ARRAY_SIZE(devclks));
|
||||
|
||||
tegra_clk_apply_init_table = tegra30_clock_apply_init_table;
|
||||
|
|
|
@ -16,56 +16,13 @@
|
|||
|
||||
#include "clk.h"
|
||||
|
||||
#define CLK_OUT_ENB_L 0x010
|
||||
#define CLK_OUT_ENB_H 0x014
|
||||
#define CLK_OUT_ENB_U 0x018
|
||||
#define CLK_OUT_ENB_V 0x360
|
||||
#define CLK_OUT_ENB_W 0x364
|
||||
#define CLK_OUT_ENB_X 0x280
|
||||
#define CLK_OUT_ENB_Y 0x298
|
||||
#define CLK_OUT_ENB_SET_L 0x320
|
||||
#define CLK_OUT_ENB_CLR_L 0x324
|
||||
#define CLK_OUT_ENB_SET_H 0x328
|
||||
#define CLK_OUT_ENB_CLR_H 0x32c
|
||||
#define CLK_OUT_ENB_SET_U 0x330
|
||||
#define CLK_OUT_ENB_CLR_U 0x334
|
||||
#define CLK_OUT_ENB_SET_V 0x440
|
||||
#define CLK_OUT_ENB_CLR_V 0x444
|
||||
#define CLK_OUT_ENB_SET_W 0x448
|
||||
#define CLK_OUT_ENB_CLR_W 0x44c
|
||||
#define CLK_OUT_ENB_SET_X 0x284
|
||||
#define CLK_OUT_ENB_CLR_X 0x288
|
||||
#define CLK_OUT_ENB_SET_Y 0x29c
|
||||
#define CLK_OUT_ENB_CLR_Y 0x2a0
|
||||
|
||||
#define RST_DEVICES_L 0x004
|
||||
#define RST_DEVICES_H 0x008
|
||||
#define RST_DEVICES_U 0x00C
|
||||
#define RST_DEVICES_V 0x358
|
||||
#define RST_DEVICES_W 0x35C
|
||||
#define RST_DEVICES_X 0x28C
|
||||
#define RST_DEVICES_Y 0x2a4
|
||||
#define RST_DEVICES_SET_L 0x300
|
||||
#define RST_DEVICES_CLR_L 0x304
|
||||
#define RST_DEVICES_SET_H 0x308
|
||||
#define RST_DEVICES_CLR_H 0x30c
|
||||
#define RST_DEVICES_SET_U 0x310
|
||||
#define RST_DEVICES_CLR_U 0x314
|
||||
#define RST_DEVICES_SET_V 0x430
|
||||
#define RST_DEVICES_CLR_V 0x434
|
||||
#define RST_DEVICES_SET_W 0x438
|
||||
#define RST_DEVICES_CLR_W 0x43c
|
||||
#define RST_DEVICES_SET_X 0x290
|
||||
#define RST_DEVICES_CLR_X 0x294
|
||||
#define RST_DEVICES_SET_Y 0x2a8
|
||||
#define RST_DEVICES_CLR_Y 0x2ac
|
||||
|
||||
/* Global data of Tegra CPU CAR ops */
|
||||
static struct tegra_cpu_car_ops dummy_car_ops;
|
||||
struct tegra_cpu_car_ops *tegra_cpu_car_ops = &dummy_car_ops;
|
||||
|
||||
int *periph_clk_enb_refcnt;
|
||||
static int periph_banks;
|
||||
static u32 *periph_state_ctx;
|
||||
static struct clk **clks;
|
||||
static int clk_num;
|
||||
static struct clk_onecell_data clk_data;
|
||||
|
@ -199,6 +156,65 @@ const struct tegra_clk_periph_regs *get_reg_bank(int clkid)
|
|||
}
|
||||
}
|
||||
|
||||
void tegra_clk_set_pllp_out_cpu(bool enable)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl_relaxed(clk_base + CLK_OUT_ENB_Y);
|
||||
if (enable)
|
||||
val |= CLK_ENB_PLLP_OUT_CPU;
|
||||
else
|
||||
val &= ~CLK_ENB_PLLP_OUT_CPU;
|
||||
|
||||
writel_relaxed(val, clk_base + CLK_OUT_ENB_Y);
|
||||
}
|
||||
|
||||
void tegra_clk_periph_suspend(void)
|
||||
{
|
||||
unsigned int i, idx;
|
||||
|
||||
idx = 0;
|
||||
for (i = 0; i < periph_banks; i++, idx++)
|
||||
periph_state_ctx[idx] =
|
||||
readl_relaxed(clk_base + periph_regs[i].enb_reg);
|
||||
|
||||
for (i = 0; i < periph_banks; i++, idx++)
|
||||
periph_state_ctx[idx] =
|
||||
readl_relaxed(clk_base + periph_regs[i].rst_reg);
|
||||
}
|
||||
|
||||
void tegra_clk_periph_resume(void)
|
||||
{
|
||||
unsigned int i, idx;
|
||||
|
||||
idx = 0;
|
||||
for (i = 0; i < periph_banks; i++, idx++)
|
||||
writel_relaxed(periph_state_ctx[idx],
|
||||
clk_base + periph_regs[i].enb_reg);
|
||||
/*
|
||||
* All non-boot peripherals will be in reset state on resume.
|
||||
* Wait for 5us of reset propagation delay before de-asserting
|
||||
* the peripherals based on the saved context.
|
||||
*/
|
||||
fence_udelay(5, clk_base);
|
||||
|
||||
for (i = 0; i < periph_banks; i++, idx++)
|
||||
writel_relaxed(periph_state_ctx[idx],
|
||||
clk_base + periph_regs[i].rst_reg);
|
||||
|
||||
fence_udelay(2, clk_base);
|
||||
}
|
||||
|
||||
static int tegra_clk_periph_ctx_init(int banks)
|
||||
{
|
||||
periph_state_ctx = kcalloc(2 * banks, sizeof(*periph_state_ctx),
|
||||
GFP_KERNEL);
|
||||
if (!periph_state_ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct clk ** __init tegra_clk_init(void __iomem *regs, int num, int banks)
|
||||
{
|
||||
clk_base = regs;
|
||||
|
@ -220,6 +236,14 @@ struct clk ** __init tegra_clk_init(void __iomem *regs, int num, int banks)
|
|||
|
||||
clk_num = num;
|
||||
|
||||
if (IS_ENABLED(CONFIG_PM_SLEEP)) {
|
||||
if (tegra_clk_periph_ctx_init(banks)) {
|
||||
kfree(periph_clk_enb_refcnt);
|
||||
kfree(clks);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return clks;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,65 @@
|
|||
#include <linux/clkdev.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define CLK_OUT_ENB_L 0x010
|
||||
#define CLK_OUT_ENB_H 0x014
|
||||
#define CLK_OUT_ENB_U 0x018
|
||||
#define CLK_OUT_ENB_V 0x360
|
||||
#define CLK_OUT_ENB_W 0x364
|
||||
#define CLK_OUT_ENB_X 0x280
|
||||
#define CLK_OUT_ENB_Y 0x298
|
||||
#define CLK_ENB_PLLP_OUT_CPU BIT(31)
|
||||
#define CLK_OUT_ENB_SET_L 0x320
|
||||
#define CLK_OUT_ENB_CLR_L 0x324
|
||||
#define CLK_OUT_ENB_SET_H 0x328
|
||||
#define CLK_OUT_ENB_CLR_H 0x32c
|
||||
#define CLK_OUT_ENB_SET_U 0x330
|
||||
#define CLK_OUT_ENB_CLR_U 0x334
|
||||
#define CLK_OUT_ENB_SET_V 0x440
|
||||
#define CLK_OUT_ENB_CLR_V 0x444
|
||||
#define CLK_OUT_ENB_SET_W 0x448
|
||||
#define CLK_OUT_ENB_CLR_W 0x44c
|
||||
#define CLK_OUT_ENB_SET_X 0x284
|
||||
#define CLK_OUT_ENB_CLR_X 0x288
|
||||
#define CLK_OUT_ENB_SET_Y 0x29c
|
||||
#define CLK_OUT_ENB_CLR_Y 0x2a0
|
||||
|
||||
#define RST_DEVICES_L 0x004
|
||||
#define RST_DEVICES_H 0x008
|
||||
#define RST_DEVICES_U 0x00C
|
||||
#define RST_DEVICES_V 0x358
|
||||
#define RST_DEVICES_W 0x35C
|
||||
#define RST_DEVICES_X 0x28C
|
||||
#define RST_DEVICES_Y 0x2a4
|
||||
#define RST_DEVICES_SET_L 0x300
|
||||
#define RST_DEVICES_CLR_L 0x304
|
||||
#define RST_DEVICES_SET_H 0x308
|
||||
#define RST_DEVICES_CLR_H 0x30c
|
||||
#define RST_DEVICES_SET_U 0x310
|
||||
#define RST_DEVICES_CLR_U 0x314
|
||||
#define RST_DEVICES_SET_V 0x430
|
||||
#define RST_DEVICES_CLR_V 0x434
|
||||
#define RST_DEVICES_SET_W 0x438
|
||||
#define RST_DEVICES_CLR_W 0x43c
|
||||
#define RST_DEVICES_SET_X 0x290
|
||||
#define RST_DEVICES_CLR_X 0x294
|
||||
#define RST_DEVICES_SET_Y 0x2a8
|
||||
#define RST_DEVICES_CLR_Y 0x2ac
|
||||
|
||||
/*
|
||||
* Tegra CLK_OUT_ENB registers have some undefined bits which are not used and
|
||||
* any accidental write of 1 to these bits can cause PSLVERR.
|
||||
* So below are the valid mask defines for each CLK_OUT_ENB register used to
|
||||
* turn ON only the valid clocks.
|
||||
*/
|
||||
#define TEGRA210_CLK_ENB_VLD_MSK_L 0xdcd7dff9
|
||||
#define TEGRA210_CLK_ENB_VLD_MSK_H 0x87d1f3e7
|
||||
#define TEGRA210_CLK_ENB_VLD_MSK_U 0xf3fed3fa
|
||||
#define TEGRA210_CLK_ENB_VLD_MSK_V 0xffc18cfb
|
||||
#define TEGRA210_CLK_ENB_VLD_MSK_W 0x793fb7ff
|
||||
#define TEGRA210_CLK_ENB_VLD_MSK_X 0x3fe66fff
|
||||
#define TEGRA210_CLK_ENB_VLD_MSK_Y 0xfc1fc7ff
|
||||
|
||||
/**
|
||||
* struct tegra_clk_sync_source - external clock source from codec
|
||||
*
|
||||
|
@ -669,6 +728,9 @@ struct clk *tegra_clk_register_periph_data(void __iomem *clk_base,
|
|||
* Flags:
|
||||
* TEGRA_DIVIDER_2 - LP cluster has additional divider. This flag indicates
|
||||
* that this is LP cluster clock.
|
||||
* TEGRA210_CPU_CLK - This flag is used to identify CPU cluster for gen5
|
||||
* super mux parent using PLLP branches. To use PLLP branches to CPU, need
|
||||
* to configure additional bit PLLP_OUT_CPU in the clock registers.
|
||||
*/
|
||||
struct tegra_clk_super_mux {
|
||||
struct clk_hw hw;
|
||||
|
@ -685,6 +747,7 @@ struct tegra_clk_super_mux {
|
|||
#define to_clk_super_mux(_hw) container_of(_hw, struct tegra_clk_super_mux, hw)
|
||||
|
||||
#define TEGRA_DIVIDER_2 BIT(0)
|
||||
#define TEGRA210_CPU_CLK BIT(1)
|
||||
|
||||
extern const struct clk_ops tegra_clk_super_ops;
|
||||
struct clk *tegra_clk_register_super_mux(const char *name,
|
||||
|
@ -829,6 +892,10 @@ u16 tegra_pll_get_fixed_mdiv(struct clk_hw *hw, unsigned long input_rate);
|
|||
int tegra_pll_p_div_to_hw(struct tegra_clk_pll *pll, u8 p_div);
|
||||
int div_frac_get(unsigned long rate, unsigned parent_rate, u8 width,
|
||||
u8 frac_width, u8 flags);
|
||||
void tegra_clk_osc_resume(void __iomem *clk_base);
|
||||
void tegra_clk_set_pllp_out_cpu(bool enable);
|
||||
void tegra_clk_periph_suspend(void);
|
||||
void tegra_clk_periph_resume(void);
|
||||
|
||||
|
||||
/* Combined read fence with delay */
|
||||
|
@ -838,4 +905,7 @@ int div_frac_get(unsigned long rate, unsigned parent_rate, u8 width,
|
|||
udelay(delay); \
|
||||
} while (0)
|
||||
|
||||
bool tegra20_clk_emc_driver_available(struct clk_hw *emc_hw);
|
||||
struct clk *tegra20_clk_register_emc(void __iomem *ioaddr, bool low_jitter);
|
||||
|
||||
#endif /* TEGRA_CLK_H */
|
||||
|
|
|
@ -85,6 +85,8 @@
|
|||
#define SCLK_EMMC_DIV50 83
|
||||
#define SCLK_DDRCLK 84
|
||||
#define SCLK_UART1_SRC 85
|
||||
#define SCLK_SDMMC_DIV 86
|
||||
#define SCLK_SDMMC_DIV50 87
|
||||
|
||||
/* dclk gates */
|
||||
#define DCLK_VOPB 150
|
||||
|
|
|
@ -337,7 +337,8 @@
|
|||
#define TEGRA124_CLK_CLK_OUT_3_MUX 308
|
||||
/* 309 */
|
||||
/* 310 */
|
||||
#define TEGRA124_CLK_SOR0_LVDS 311
|
||||
#define TEGRA124_CLK_SOR0_LVDS 311 /* deprecated */
|
||||
#define TEGRA124_CLK_SOR0_OUT 311
|
||||
#define TEGRA124_CLK_XUSB_SS_DIV2 312
|
||||
|
||||
#define TEGRA124_CLK_PLL_M_UD 313
|
||||
|
|
|
@ -308,8 +308,8 @@
|
|||
#define TEGRA210_CLK_CLK_OUT_2 278
|
||||
#define TEGRA210_CLK_CLK_OUT_3 279
|
||||
#define TEGRA210_CLK_BLINK 280
|
||||
/* 281 */
|
||||
#define TEGRA210_CLK_SOR1_SRC 282
|
||||
#define TEGRA210_CLK_SOR0_LVDS 281 /* deprecated */
|
||||
#define TEGRA210_CLK_SOR0_OUT 281
|
||||
#define TEGRA210_CLK_SOR1_OUT 282
|
||||
/* 283 */
|
||||
#define TEGRA210_CLK_XUSB_HOST_SRC 284
|
||||
|
@ -391,7 +391,7 @@
|
|||
#define TEGRA210_CLK_CLK_OUT_3_MUX 358
|
||||
#define TEGRA210_CLK_DSIA_MUX 359
|
||||
#define TEGRA210_CLK_DSIB_MUX 360
|
||||
#define TEGRA210_CLK_SOR0_LVDS 361
|
||||
/* 361 */
|
||||
#define TEGRA210_CLK_XUSB_SS_DIV2 362
|
||||
|
||||
#define TEGRA210_CLK_PLL_M_UD 363
|
||||
|
|
|
@ -818,6 +818,7 @@ unsigned int clk_hw_get_num_parents(const struct clk_hw *hw);
|
|||
struct clk_hw *clk_hw_get_parent(const struct clk_hw *hw);
|
||||
struct clk_hw *clk_hw_get_parent_by_index(const struct clk_hw *hw,
|
||||
unsigned int index);
|
||||
int clk_hw_get_parent_index(struct clk_hw *hw);
|
||||
int clk_hw_set_parent(struct clk_hw *hw, struct clk_hw *new_parent);
|
||||
unsigned int __clk_get_enable_count(struct clk *clk);
|
||||
unsigned long clk_hw_get_rate(const struct clk_hw *hw);
|
||||
|
|
|
@ -108,6 +108,19 @@ static inline void tegra_cpu_clock_resume(void)
|
|||
|
||||
tegra_cpu_car_ops->resume();
|
||||
}
|
||||
#else
|
||||
static inline bool tegra_cpu_rail_off_ready(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void tegra_cpu_clock_suspend(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void tegra_cpu_clock_resume(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
extern void tegra210_xusb_pll_hw_control_enable(void);
|
||||
|
@ -119,4 +132,15 @@ extern void tegra210_put_utmipll_in_iddq(void);
|
|||
extern void tegra210_put_utmipll_out_iddq(void);
|
||||
extern int tegra210_clk_handle_mbist_war(unsigned int id);
|
||||
|
||||
struct clk;
|
||||
|
||||
typedef long (tegra20_clk_emc_round_cb)(unsigned long rate,
|
||||
unsigned long min_rate,
|
||||
unsigned long max_rate,
|
||||
void *arg);
|
||||
|
||||
void tegra20_clk_set_emc_round_callback(tegra20_clk_emc_round_cb *round_cb,
|
||||
void *cb_arg);
|
||||
int tegra20_clk_prepare_emc_mc_same_freq(struct clk *emc_clk, bool same);
|
||||
|
||||
#endif /* __LINUX_CLK_TEGRA_H_ */
|
||||
|
|
Loading…
Reference in New Issue