aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clk/tegra/clk-tegra30.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/tegra/clk-tegra30.c')
-rw-r--r--drivers/clk/tegra/clk-tegra30.c170
1 files changed, 108 insertions, 62 deletions
diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c
index b20891489e11..60f1534711f1 100644
--- a/drivers/clk/tegra/clk-tegra30.c
+++ b/drivers/clk/tegra/clk-tegra30.c
@@ -7,8 +7,11 @@
#include <linux/delay.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
+#include <linux/init.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
#include <linux/clk/tegra.h>
#include <soc/tegra/pmc.h>
@@ -499,6 +502,8 @@ static struct tegra_clk_pll_params pll_x_params __ro_after_init = {
.freq_table = pll_x_freq_table,
.flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_DCCON |
TEGRA_PLL_USE_LOCK | TEGRA_PLL_HAS_LOCK_ENABLE,
+ .pre_rate_change = tegra_cclk_pre_pllx_rate_change,
+ .post_rate_change = tegra_cclk_post_pllx_rate_change,
};
static struct tegra_clk_pll_params pll_e_params __ro_after_init = {
@@ -530,7 +535,7 @@ static unsigned long tegra30_input_freq[] = {
[12] = 26000000,
};
-static struct tegra_devclk devclks[] __initdata = {
+static struct tegra_devclk devclks[] = {
{ .con_id = "pll_c", .dt_id = TEGRA30_CLK_PLL_C },
{ .con_id = "pll_c_out1", .dt_id = TEGRA30_CLK_PLL_C_OUT1 },
{ .con_id = "pll_p", .dt_id = TEGRA30_CLK_PLL_P },
@@ -569,10 +574,9 @@ static struct tegra_devclk devclks[] __initdata = {
{ .con_id = "audio3_2x", .dt_id = TEGRA30_CLK_AUDIO3_2X },
{ .con_id = "audio4_2x", .dt_id = TEGRA30_CLK_AUDIO4_2X },
{ .con_id = "spdif_2x", .dt_id = TEGRA30_CLK_SPDIF_2X },
- { .con_id = "extern1", .dev_id = "clk_out_1", .dt_id = TEGRA30_CLK_EXTERN1 },
- { .con_id = "extern2", .dev_id = "clk_out_2", .dt_id = TEGRA30_CLK_EXTERN2 },
- { .con_id = "extern3", .dev_id = "clk_out_3", .dt_id = TEGRA30_CLK_EXTERN3 },
- { .con_id = "blink", .dt_id = TEGRA30_CLK_BLINK },
+ { .con_id = "extern1", .dt_id = TEGRA30_CLK_EXTERN1 },
+ { .con_id = "extern2", .dt_id = TEGRA30_CLK_EXTERN2 },
+ { .con_id = "extern3", .dt_id = TEGRA30_CLK_EXTERN3 },
{ .con_id = "cclk_g", .dt_id = TEGRA30_CLK_CCLK_G },
{ .con_id = "cclk_lp", .dt_id = TEGRA30_CLK_CCLK_LP },
{ .con_id = "sclk", .dt_id = TEGRA30_CLK_SCLK },
@@ -581,8 +585,9 @@ static struct tegra_devclk devclks[] __initdata = {
{ .con_id = "twd", .dt_id = TEGRA30_CLK_TWD },
{ .con_id = "emc", .dt_id = TEGRA30_CLK_EMC },
{ .con_id = "clk_32k", .dt_id = TEGRA30_CLK_CLK_32K },
- { .con_id = "clk_m_div2", .dt_id = TEGRA30_CLK_CLK_M_DIV2 },
- { .con_id = "clk_m_div4", .dt_id = TEGRA30_CLK_CLK_M_DIV4 },
+ { .con_id = "osc", .dt_id = TEGRA30_CLK_OSC },
+ { .con_id = "osc_div2", .dt_id = TEGRA30_CLK_OSC_DIV2 },
+ { .con_id = "osc_div4", .dt_id = TEGRA30_CLK_OSC_DIV4 },
{ .con_id = "cml0", .dt_id = TEGRA30_CLK_CML0 },
{ .con_id = "cml1", .dt_id = TEGRA30_CLK_CML1 },
{ .con_id = "clk_m", .dt_id = TEGRA30_CLK_CLK_M },
@@ -683,8 +688,9 @@ static struct tegra_devclk devclks[] __initdata = {
static struct tegra_clk tegra30_clks[tegra_clk_max] __initdata = {
[tegra_clk_clk_32k] = { .dt_id = TEGRA30_CLK_CLK_32K, .present = true },
[tegra_clk_clk_m] = { .dt_id = TEGRA30_CLK_CLK_M, .present = true },
- [tegra_clk_clk_m_div2] = { .dt_id = TEGRA30_CLK_CLK_M_DIV2, .present = true },
- [tegra_clk_clk_m_div4] = { .dt_id = TEGRA30_CLK_CLK_M_DIV4, .present = true },
+ [tegra_clk_osc] = { .dt_id = TEGRA30_CLK_OSC, .present = true },
+ [tegra_clk_osc_div2] = { .dt_id = TEGRA30_CLK_OSC_DIV2, .present = true },
+ [tegra_clk_osc_div4] = { .dt_id = TEGRA30_CLK_OSC_DIV4, .present = true },
[tegra_clk_pll_ref] = { .dt_id = TEGRA30_CLK_PLL_REF, .present = true },
[tegra_clk_spdif_in_sync] = { .dt_id = TEGRA30_CLK_SPDIF_IN_SYNC, .present = true },
[tegra_clk_i2s0_sync] = { .dt_id = TEGRA30_CLK_I2S0_SYNC, .present = true },
@@ -711,13 +717,6 @@ static struct tegra_clk tegra30_clks[tegra_clk_max] __initdata = {
[tegra_clk_audio3_2x] = { .dt_id = TEGRA30_CLK_AUDIO3_2X, .present = true },
[tegra_clk_audio4_2x] = { .dt_id = TEGRA30_CLK_AUDIO4_2X, .present = true },
[tegra_clk_spdif_2x] = { .dt_id = TEGRA30_CLK_SPDIF_2X, .present = true },
- [tegra_clk_clk_out_1] = { .dt_id = TEGRA30_CLK_CLK_OUT_1, .present = true },
- [tegra_clk_clk_out_2] = { .dt_id = TEGRA30_CLK_CLK_OUT_2, .present = true },
- [tegra_clk_clk_out_3] = { .dt_id = TEGRA30_CLK_CLK_OUT_3, .present = true },
- [tegra_clk_blink] = { .dt_id = TEGRA30_CLK_BLINK, .present = true },
- [tegra_clk_clk_out_1_mux] = { .dt_id = TEGRA30_CLK_CLK_OUT_1_MUX, .present = true },
- [tegra_clk_clk_out_2_mux] = { .dt_id = TEGRA30_CLK_CLK_OUT_2_MUX, .present = true },
- [tegra_clk_clk_out_3_mux] = { .dt_id = TEGRA30_CLK_CLK_OUT_3_MUX, .present = true },
[tegra_clk_hclk] = { .dt_id = TEGRA30_CLK_HCLK, .present = true },
[tegra_clk_pclk] = { .dt_id = TEGRA30_CLK_PCLK, .present = true },
[tegra_clk_i2s0] = { .dt_id = TEGRA30_CLK_I2S0, .present = true },
@@ -816,11 +815,6 @@ static void __init tegra30_pll_init(void)
{
struct clk *clk;
- /* PLLC */
- clk = tegra_clk_register_pll("pll_c", "pll_ref", clk_base, pmc_base, 0,
- &pll_c_params, NULL);
- clks[TEGRA30_CLK_PLL_C] = clk;
-
/* PLLC_OUT1 */
clk = tegra_clk_register_divider("pll_c_out1_div", "pll_c",
clk_base + PLLC_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
@@ -830,11 +824,6 @@ static void __init tegra30_pll_init(void)
0, NULL);
clks[TEGRA30_CLK_PLL_C_OUT1] = clk;
- /* PLLM */
- clk = tegra_clk_register_pll("pll_m", "pll_ref", clk_base, pmc_base,
- CLK_SET_RATE_GATE, &pll_m_params, NULL);
- clks[TEGRA30_CLK_PLL_M] = clk;
-
/* PLLM_OUT1 */
clk = tegra_clk_register_divider("pll_m_out1_div", "pll_m",
clk_base + PLLM_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
@@ -884,9 +873,6 @@ static void __init tegra30_pll_init(void)
ARRAY_SIZE(pll_e_parents),
CLK_SET_RATE_NO_REPARENT,
clk_base + PLLE_AUX, 2, 1, 0, NULL);
- clk = tegra_clk_register_plle("pll_e", "pll_e_mux", clk_base, pmc_base,
- CLK_GET_RATE_NOCACHE, &pll_e_params, NULL);
- clks[TEGRA30_CLK_PLL_E] = clk;
}
static const char *cclk_g_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m",
@@ -932,11 +918,11 @@ static void __init tegra30_super_clk_init(void)
clk_register_clkdev(clk, "pll_p_out4_cclkg", NULL);
/* CCLKG */
- clk = tegra_clk_register_super_mux("cclk_g", cclk_g_parents,
+ clk = tegra_clk_register_super_cclk("cclk_g", cclk_g_parents,
ARRAY_SIZE(cclk_g_parents),
- CLK_SET_RATE_PARENT,
+ CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
clk_base + CCLKG_BURST_POLICY,
- 0, 4, 0, 0, NULL);
+ 0, NULL);
clks[TEGRA30_CLK_CCLK_G] = clk;
/*
@@ -975,14 +961,6 @@ static void __init tegra30_super_clk_init(void)
NULL);
clks[TEGRA30_CLK_CCLK_LP] = clk;
- /* SCLK */
- clk = tegra_clk_register_super_mux("sclk", sclk_parents,
- ARRAY_SIZE(sclk_parents),
- CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
- clk_base + SCLK_BURST_POLICY,
- 0, 4, 0, 0, NULL);
- clks[TEGRA30_CLK_SCLK] = clk;
-
/* twd */
clk = clk_register_fixed_factor(NULL, "twd", "cclk_g",
CLK_SET_RATE_PARENT, 1, 2);
@@ -1010,7 +988,7 @@ static struct tegra_periph_init_data tegra_periph_clk_list[] = {
TEGRA_INIT_DATA_MUX("dam0", mux_pllacp_clkm, CLK_SOURCE_DAM0, 108, 0, TEGRA30_CLK_DAM0),
TEGRA_INIT_DATA_MUX("dam1", mux_pllacp_clkm, CLK_SOURCE_DAM1, 109, 0, TEGRA30_CLK_DAM1),
TEGRA_INIT_DATA_MUX("dam2", mux_pllacp_clkm, CLK_SOURCE_DAM2, 110, 0, TEGRA30_CLK_DAM2),
- TEGRA_INIT_DATA_INT("3d2", mux_pllmcpa, CLK_SOURCE_3D2, 98, TEGRA_PERIPH_MANUAL_RESET, TEGRA30_CLK_GR3D2),
+ TEGRA_INIT_DATA_INT("3d2", mux_pllmcpa, CLK_SOURCE_3D2, 98, 0, TEGRA30_CLK_GR3D2),
TEGRA_INIT_DATA_INT("se", mux_pllpcm_clkm, CLK_SOURCE_SE, 127, 0, TEGRA30_CLK_SE),
TEGRA_INIT_DATA_MUX8("hdmi", mux_pllpmdacd2_clkm, CLK_SOURCE_HDMI, 51, 0, TEGRA30_CLK_HDMI),
TEGRA_INIT_DATA("pwm", NULL, NULL, pwm_parents, CLK_SOURCE_PWM, 28, 2, 0, 0, 8, 1, 0, 17, TEGRA_PERIPH_ON_APB, TEGRA30_CLK_PWM),
@@ -1108,12 +1086,9 @@ static void tegra30_cpu_out_of_reset(u32 cpu)
static void tegra30_enable_cpu_clock(u32 cpu)
{
- unsigned int reg;
-
writel(CPU_CLOCK(cpu),
clk_base + TEGRA30_CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR);
- reg = readl(clk_base +
- TEGRA30_CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR);
+ readl(clk_base + TEGRA30_CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR);
}
static void tegra30_disable_cpu_clock(u32 cpu)
@@ -1221,18 +1196,14 @@ static struct tegra_cpu_car_ops tegra30_cpu_car_ops = {
#endif
};
-static struct tegra_clk_init_table init_table[] __initdata = {
+static struct tegra_clk_init_table init_table[] = {
{ TEGRA30_CLK_UARTA, TEGRA30_CLK_PLL_P, 408000000, 0 },
{ TEGRA30_CLK_UARTB, TEGRA30_CLK_PLL_P, 408000000, 0 },
{ TEGRA30_CLK_UARTC, TEGRA30_CLK_PLL_P, 408000000, 0 },
{ TEGRA30_CLK_UARTD, TEGRA30_CLK_PLL_P, 408000000, 0 },
{ TEGRA30_CLK_UARTE, TEGRA30_CLK_PLL_P, 408000000, 0 },
- { TEGRA30_CLK_PLL_A, TEGRA30_CLK_CLK_MAX, 564480000, 1 },
- { TEGRA30_CLK_PLL_A_OUT0, TEGRA30_CLK_CLK_MAX, 11289600, 1 },
- { TEGRA30_CLK_EXTERN1, TEGRA30_CLK_PLL_A_OUT0, 0, 1 },
- { TEGRA30_CLK_CLK_OUT_1_MUX, TEGRA30_CLK_EXTERN1, 0, 0 },
- { TEGRA30_CLK_CLK_OUT_1, TEGRA30_CLK_CLK_MAX, 0, 1 },
- { TEGRA30_CLK_BLINK, TEGRA30_CLK_CLK_MAX, 0, 1 },
+ { TEGRA30_CLK_PLL_A, TEGRA30_CLK_CLK_MAX, 564480000, 0 },
+ { TEGRA30_CLK_PLL_A_OUT0, TEGRA30_CLK_CLK_MAX, 11289600, 0 },
{ TEGRA30_CLK_I2S0, TEGRA30_CLK_PLL_A_OUT0, 11289600, 0 },
{ TEGRA30_CLK_I2S1, TEGRA30_CLK_PLL_A_OUT0, 11289600, 0 },
{ TEGRA30_CLK_I2S2, TEGRA30_CLK_PLL_A_OUT0, 11289600, 0 },
@@ -1256,7 +1227,7 @@ static struct tegra_clk_init_table init_table[] __initdata = {
{ TEGRA30_CLK_GR3D, TEGRA30_CLK_PLL_C, 300000000, 0 },
{ TEGRA30_CLK_GR3D2, TEGRA30_CLK_PLL_C, 300000000, 0 },
{ TEGRA30_CLK_PLL_U, TEGRA30_CLK_CLK_MAX, 480000000, 0 },
- { TEGRA30_CLK_VDE, TEGRA30_CLK_PLL_C, 600000000, 0 },
+ { TEGRA30_CLK_VDE, TEGRA30_CLK_PLL_C, 300000000, 0 },
{ TEGRA30_CLK_SPDIF_IN_SYNC, TEGRA30_CLK_CLK_MAX, 24000000, 0 },
{ TEGRA30_CLK_I2S0_SYNC, TEGRA30_CLK_CLK_MAX, 24000000, 0 },
{ TEGRA30_CLK_I2S1_SYNC, TEGRA30_CLK_CLK_MAX, 24000000, 0 },
@@ -1264,15 +1235,13 @@ static struct tegra_clk_init_table init_table[] __initdata = {
{ TEGRA30_CLK_I2S3_SYNC, TEGRA30_CLK_CLK_MAX, 24000000, 0 },
{ TEGRA30_CLK_I2S4_SYNC, TEGRA30_CLK_CLK_MAX, 24000000, 0 },
{ TEGRA30_CLK_VIMCLK_SYNC, TEGRA30_CLK_CLK_MAX, 24000000, 0 },
+ { TEGRA30_CLK_HDA, TEGRA30_CLK_PLL_P, 102000000, 0 },
+ { TEGRA30_CLK_HDA2CODEC_2X, TEGRA30_CLK_PLL_P, 48000000, 0 },
+ { TEGRA30_CLK_PWM, TEGRA30_CLK_PLL_P, 48000000, 0 },
/* must be the last entry */
{ TEGRA30_CLK_CLK_MAX, TEGRA30_CLK_CLK_MAX, 0, 0 },
};
-static void __init tegra30_clock_apply_init_table(void)
-{
- tegra_init_from_table(init_table, clks, TEGRA30_CLK_CLK_MAX);
-}
-
/*
* Some clocks may be used by different drivers depending on the board
* configuration. List those here to register them twice in the clock lookup
@@ -1303,12 +1272,24 @@ static struct tegra_audio_clk_info tegra30_audio_plls[] = {
{ "pll_a", &pll_a_params, tegra_clk_pll_a, "pll_p_out1" },
};
+static bool tegra30_car_initialized;
+
static struct clk *tegra30_clk_src_onecell_get(struct of_phandle_args *clkspec,
void *data)
{
struct clk_hw *hw;
struct clk *clk;
+ /*
+ * Timer clocks are needed early, the rest of the clocks shouldn't be
+ * available to device drivers until clock tree is fully initialized.
+ */
+ if (clkspec->args[0] != TEGRA30_CLK_RTC &&
+ clkspec->args[0] != TEGRA30_CLK_TWD &&
+ clkspec->args[0] != TEGRA30_CLK_TIMER &&
+ !tegra30_car_initialized)
+ return ERR_PTR(-EPROBE_DEFER);
+
clk = of_clk_src_onecell_get(clkspec, data);
if (IS_ERR(clk))
return clk;
@@ -1340,6 +1321,7 @@ static void __init tegra30_clock_init(struct device_node *np)
}
pmc_base = of_iomap(node, 0);
+ of_node_put(node);
if (!pmc_base) {
pr_err("Can't map pmc registers\n");
BUG();
@@ -1362,15 +1344,79 @@ static void __init tegra30_clock_init(struct device_node *np)
tegra_audio_clk_init(clk_base, pmc_base, tegra30_clks,
tegra30_audio_plls,
ARRAY_SIZE(tegra30_audio_plls), 24000000);
- tegra_pmc_clk_init(pmc_base, tegra30_clks);
tegra_init_dup_clks(tegra_clk_duplicates, clks, TEGRA30_CLK_CLK_MAX);
tegra_add_of_provider(np, tegra30_clk_src_onecell_get);
+
+ tegra_cpu_car_ops = &tegra30_cpu_car_ops;
+}
+CLK_OF_DECLARE_DRIVER(tegra30, "nvidia,tegra30-car", tegra30_clock_init);
+
+/*
+ * Clocks that use runtime PM can't be created at the tegra30_clock_init
+ * time because drivers' base isn't initialized yet, and thus platform
+ * devices can't be created for the clocks. Hence we need to split the
+ * registration of the clocks into two phases. The first phase registers
+ * essential clocks which don't require RPM and are actually used during
+ * early boot. The second phase registers clocks which use RPM and this
+ * is done when device drivers' core API is ready.
+ */
+static int tegra30_car_probe(struct platform_device *pdev)
+{
+ struct clk *clk;
+
+ /* PLLC */
+ clk = tegra_clk_register_pll("pll_c", "pll_ref", clk_base, pmc_base, 0,
+ &pll_c_params, NULL);
+ clks[TEGRA30_CLK_PLL_C] = clk;
+
+ /* PLLE */
+ clk = tegra_clk_register_plle("pll_e", "pll_e_mux", clk_base, pmc_base,
+ CLK_GET_RATE_NOCACHE, &pll_e_params, NULL);
+ clks[TEGRA30_CLK_PLL_E] = clk;
+
+ /* PLLM */
+ clk = tegra_clk_register_pll("pll_m", "pll_ref", clk_base, pmc_base,
+ CLK_SET_RATE_GATE, &pll_m_params, NULL);
+ clks[TEGRA30_CLK_PLL_M] = clk;
+
+ /* SCLK */
+ clk = tegra_clk_register_super_mux("sclk", sclk_parents,
+ ARRAY_SIZE(sclk_parents),
+ CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+ clk_base + SCLK_BURST_POLICY,
+ 0, 4, 0, 0, NULL);
+ clks[TEGRA30_CLK_SCLK] = clk;
+
tegra_register_devclks(devclks, ARRAY_SIZE(devclks));
+ tegra_init_from_table(init_table, clks, TEGRA30_CLK_CLK_MAX);
+ tegra30_car_initialized = true;
- tegra_clk_apply_init_table = tegra30_clock_apply_init_table;
+ return 0;
+}
- tegra_cpu_car_ops = &tegra30_cpu_car_ops;
+static const struct of_device_id tegra30_car_match[] = {
+ { .compatible = "nvidia,tegra30-car" },
+ { }
+};
+
+static struct platform_driver tegra30_car_driver = {
+ .driver = {
+ .name = "tegra30-car",
+ .of_match_table = tegra30_car_match,
+ .suppress_bind_attrs = true,
+ },
+ .probe = tegra30_car_probe,
+};
+
+/*
+ * Clock driver must be registered before memory controller driver,
+ * which doesn't support deferred probing for today and is registered
+ * from arch init-level.
+ */
+static int tegra30_car_init(void)
+{
+ return platform_driver_register(&tegra30_car_driver);
}
-CLK_OF_DECLARE(tegra30, "nvidia,tegra30-car", tegra30_clock_init);
+postcore_initcall(tegra30_car_init);