diff options
author | Alex Frid <afrid@nvidia.com> | 2013-05-03 20:08:12 -0700 |
---|---|---|
committer | Harshada Kale <hkale@nvidia.com> | 2013-09-12 02:37:18 -0700 |
commit | 9eef509f9065f496b8fedb7b3b0d76b43f85c430 (patch) | |
tree | c317d6e484749d76ba8ea98d42a31a11824ffa07 | |
parent | 9857fa2b0f214d79bab28069887e72d98481f0a1 (diff) |
ARM: tegra11: clock: Add host1x shared bus
Change-Id: Ie63f856727f9ba9f93e6c75b7bd5fb80357448a4
Signed-off-by: Alex Frid <afrid@nvidia.com>
Reviewed-on: http://git-master/r/250561
(cherry picked from commit 2899d88dd8afa1971f2c1b09b7039852a65a5f4f)
Reviewed-on: http://git-master/r/253678
Tested-by: Shaoming Feng <shaomingf@nvidia.com>
GVS: Gerrit_Virtual_Submit
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
-rw-r--r-- | arch/arm/mach-tegra/clock.h | 3 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra11_clocks.c | 142 |
2 files changed, 143 insertions, 2 deletions
diff --git a/arch/arm/mach-tegra/clock.h b/arch/arm/mach-tegra/clock.h index a0ed791447e0..5a471dc165e2 100644 --- a/arch/arm/mach-tegra/clock.h +++ b/arch/arm/mach-tegra/clock.h @@ -178,6 +178,9 @@ struct clk { unsigned int clk_num; u32 src_mask; u32 src_shift; + struct clk *pll_low; + struct clk *pll_high; + unsigned long threshold; } periph; struct { unsigned long input_min; diff --git a/arch/arm/mach-tegra/tegra11_clocks.c b/arch/arm/mach-tegra/tegra11_clocks.c index 0e2d8d402590..9fbffb9dc6a9 100644 --- a/arch/arm/mach-tegra/tegra11_clocks.c +++ b/arch/arm/mach-tegra/tegra11_clocks.c @@ -4055,6 +4055,118 @@ static struct clk_ops tegra_periph_clk_ops = { .reset = &tegra11_periph_clk_reset, }; +/* x1 shared bus ops */ +static long tegra11_1xbus_round_updown(struct clk *c, unsigned long rate, + bool up) +{ + int divider; + unsigned long source_rate, round_rate; + struct clk *new_parent; + + rate = max(rate, c->min_rate); + + new_parent = (rate <= c->u.periph.threshold) ? + c->u.periph.pll_low : c->u.periph.pll_high; + source_rate = clk_get_rate(new_parent); + + divider = clk_div71_get_divider(source_rate, rate, c->flags, + up ? ROUND_DIVIDER_DOWN : ROUND_DIVIDER_UP); + + if (divider < 0) + return c->min_rate; + + round_rate = source_rate * 2 / (divider + 2); + + if (round_rate > c->max_rate) { + divider += c->flags & DIV_U71_INT ? 2 : 1; +#if !DIVIDER_1_5_ALLOWED + divider = max(2, divider); +#endif + round_rate = source_rate * 2 / (divider + 2); + } + + if (new_parent == c->u.periph.pll_high) { + /* Prevent oscillation across threshold */ + if (round_rate <= c->u.periph.threshold) + round_rate = c->u.periph.threshold; + } + return round_rate; +} + +static long tegra11_1xbus_round_rate(struct clk *c, unsigned long rate) +{ + return tegra11_1xbus_round_updown(c, rate, true); +} + +static int tegra11_1xbus_set_rate(struct clk *c, unsigned long rate) +{ + /* Compensate rate truncating during rounding */ + return tegra11_periph_clk_set_rate(c, rate + 1); +} + +static int tegra11_clk_1xbus_update(struct clk *c) +{ + int ret; + struct clk *new_parent; + unsigned long rate, old_rate; + + if (detach_shared_bus) + return 0; + + rate = tegra11_clk_shared_bus_update(c, NULL, NULL, NULL); + + old_rate = clk_get_rate_locked(c); + if (rate == old_rate) + return 0; + + new_parent = (rate <= c->u.periph.threshold) ? + c->u.periph.pll_low : c->u.periph.pll_high; + + /* + * When changing parents to lower one, set 1st max possible rate to + * avoid transition with dip. + * + * FIXME: the transition procedure below works for current Tegra11 + * clock limits. In general parent switch may fail because intermediate + * rate violates max limit. + */ + if ((new_parent != c->parent) && (new_parent == c->u.periph.pll_low)) { + ret = clk_set_rate_locked(c, c->max_rate); + if (ret) { + pr_err("Failed to set %s rate to %lu\n", + c->name, c->max_rate); + return ret; + } + } + + if (new_parent != c->parent) { + ret = clk_set_parent_locked(c, new_parent); + if (ret) { + pr_err("Failed to set %s parent %s\n", + c->name, new_parent->name); + return ret; + } + } + + ret = clk_set_rate_locked(c, rate); + if (ret) { + pr_err("Failed to set %s rate to %lu\n", c->name, rate); + return ret; + } + return 0; + +} + +static struct clk_ops tegra_1xbus_clk_ops = { + .init = &tegra11_periph_clk_init, + .enable = &tegra11_periph_clk_enable, + .disable = &tegra11_periph_clk_disable, + .set_parent = &tegra11_periph_clk_set_parent, + .set_rate = &tegra11_1xbus_set_rate, + .round_rate = &tegra11_1xbus_round_rate, + .reset = &tegra11_periph_clk_reset, + .shared_bus_update = &tegra11_clk_1xbus_update, +}; #if !defined(CONFIG_TEGRA_SIMULATION_PLATFORM) /* msenc clock propagation WAR for bug 1005168 */ @@ -6342,6 +6454,28 @@ static struct clk tegra_clk_emc = { .rate_change_nh = &emc_rate_change_nh, }; +static struct raw_notifier_head host1x_rate_change_nh; + +static struct clk tegra_clk_host1x = { + .name = "host1x", + .ops = &tegra_1xbus_clk_ops, + .reg = 0x180, + .inputs = mux_pllm_pllc_pllp_plla, + .flags = MUX | DIV_U71 | DIV_U71_INT, + .max_rate = 384000000, + .min_rate = 12000000, + .u.periph = { + .clk_num = 28, + .pll_low = &tegra_pll_p, +#ifdef CONFIG_TEGRA_PLLM_SCALED + .pll_high = &tegra_pll_c, +#else + .pll_high = &tegra_pll_m, +#endif + }, + .rate_change_nh = &host1x_rate_change_nh, +}; + #ifdef CONFIG_TEGRA_DUAL_CBUS static struct raw_notifier_head c2bus_rate_change_nh; @@ -6572,7 +6706,6 @@ struct clk tegra_list_clks[] = { PERIPH_CLK_EX("msenc", "msenc", NULL, 91, 0x1f0, 600000000, mux_pllm_pllc2_c_c3_pllp_plla, MUX | MUX8 | DIV_U71 | DIV_U71_INT, &tegra_msenc_clk_ops), #endif PERIPH_CLK("tsec", "tsec", NULL, 83, 0x1f4, 600000000, mux_pllp_pllc2_c_c3_pllm_clkm, MUX | MUX8 | DIV_U71 | DIV_U71_INT), - PERIPH_CLK("host1x", "host1x", NULL, 28, 0x180, 384000000, mux_pllm_pllc2_c_c3_pllp_plla, MUX | MUX8 | DIV_U71 | DIV_U71_INT), PERIPH_CLK_EX("dtv", "dtv", NULL, 79, 0x1dc, 250000000, mux_clk_m, PERIPH_ON_APB, &tegra_dtv_clk_ops), PERIPH_CLK("hdmi", "hdmi", NULL, 51, 0x18c, 297000000, mux_pllp_pllm_plld_plla_pllc_plld2_clkm, MUX | MUX8 | DIV_U71), PERIPH_CLK("disp1", "tegradc.0", NULL, 27, 0x138, 600000000, mux_pllp_pllm_plld_plla_pllc_plld2_clkm, MUX | MUX8), @@ -6689,6 +6822,8 @@ struct clk tegra_list_clks[] = { SHARED_CLK("cap.profile.cbus", "profile.cbus", NULL, &tegra_clk_cbus, NULL, 0, SHARED_CEILING), SHARED_CLK("battery.cbus", "battery_edp", "gpu", &tegra_clk_cbus, NULL, 0, SHARED_CEILING), #endif + SHARED_CLK("nv_host1x", "tegra_host1x", "host1x", &tegra_clk_host1x, 0, 0, 0), + SHARED_CLK("vi_host1x", "tegra_vi", "host1x", &tegra_clk_host1x, 0, 0, 0), }; @@ -6812,7 +6947,6 @@ struct clk_duplicate tegra_clk_duplicates[] = { CLK_DUPLICATE("vde.cbus", "nvavp", "vde"), CLK_DUPLICATE("i2c5", "tegra_cl_dvfs", "i2c"), CLK_DUPLICATE("cpu_g", "tegra_cl_dvfs", "safe_dvfs"), - CLK_DUPLICATE("host1x", "tegra_host1x", "host1x"), CLK_DUPLICATE("epp.cbus", "tegra_isp", "epp"), }; @@ -6863,6 +6997,7 @@ struct clk *tegra_ptr_clks[] = { &tegra_clk_cop, &tegra_clk_sbus_cmplx, &tegra_clk_emc, + &tegra_clk_host1x, #ifdef CONFIG_TEGRA_DUAL_CBUS &tegra_clk_c2bus, &tegra_clk_c3bus, @@ -6943,16 +7078,19 @@ static void tegra11_pllp_init_dependencies(unsigned long pllp_rate) tegra_pll_p_out1.u.pll_div.default_rate = 28800000; tegra_pll_p_out3.u.pll_div.default_rate = 72000000; tegra_clk_sbus_cmplx.u.system.threshold = 108000000; + tegra_clk_host1x.u.periph.threshold = 108000000; break; case 408000000: tegra_pll_p_out1.u.pll_div.default_rate = 9600000; tegra_pll_p_out3.u.pll_div.default_rate = 102000000; tegra_clk_sbus_cmplx.u.system.threshold = 204000000; + tegra_clk_host1x.u.periph.threshold = 204000000; break; case 204000000: tegra_pll_p_out1.u.pll_div.default_rate = 4800000; tegra_pll_p_out3.u.pll_div.default_rate = 102000000; tegra_clk_sbus_cmplx.u.system.threshold = 204000000; + tegra_clk_host1x.u.periph.threshold = 204000000; break; default: pr_err("tegra: PLLP rate: %lu is not supported\n", pllp_rate); |