ARM: tegra: clocks: Add emc scaling
Add clock ops on the emc peripheral clock that call into the emc driver to update the memory controller registers for the new frequency. Tegra has an interlock between the clock controller and the memory controller that prevents the new register values from taking effect until the clock frequency update occurs. Acked-by: Olof Johansson <olof@lixom.net> Signed-off-by: Colin Cross <ccross@android.com>
This commit is contained in:
parent
efdf72ad5c
commit
6d2968284f
|
@ -31,6 +31,7 @@
|
|||
|
||||
#include "clock.h"
|
||||
#include "fuse.h"
|
||||
#include "tegra2_emc.h"
|
||||
|
||||
#define RST_DEVICES 0x004
|
||||
#define RST_DEVICES_SET 0x300
|
||||
|
@ -1051,6 +1052,55 @@ static struct clk_ops tegra_periph_clk_ops = {
|
|||
.reset = &tegra2_periph_clk_reset,
|
||||
};
|
||||
|
||||
/* External memory controller clock ops */
|
||||
static void tegra2_emc_clk_init(struct clk *c)
|
||||
{
|
||||
tegra2_periph_clk_init(c);
|
||||
c->max_rate = clk_get_rate_locked(c);
|
||||
}
|
||||
|
||||
static long tegra2_emc_clk_round_rate(struct clk *c, unsigned long rate)
|
||||
{
|
||||
long new_rate = rate;
|
||||
|
||||
new_rate = tegra_emc_round_rate(new_rate);
|
||||
if (new_rate < 0)
|
||||
return c->max_rate;
|
||||
|
||||
BUG_ON(new_rate != tegra2_periph_clk_round_rate(c, new_rate));
|
||||
|
||||
return new_rate;
|
||||
}
|
||||
|
||||
static int tegra2_emc_clk_set_rate(struct clk *c, unsigned long rate)
|
||||
{
|
||||
int ret;
|
||||
/*
|
||||
* The Tegra2 memory controller has an interlock with the clock
|
||||
* block that allows memory shadowed registers to be updated,
|
||||
* and then transfer them to the main registers at the same
|
||||
* time as the clock update without glitches.
|
||||
*/
|
||||
ret = tegra_emc_set_rate(rate);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = tegra2_periph_clk_set_rate(c, rate);
|
||||
udelay(1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct clk_ops tegra_emc_clk_ops = {
|
||||
.init = &tegra2_emc_clk_init,
|
||||
.enable = &tegra2_periph_clk_enable,
|
||||
.disable = &tegra2_periph_clk_disable,
|
||||
.set_parent = &tegra2_periph_clk_set_parent,
|
||||
.set_rate = &tegra2_emc_clk_set_rate,
|
||||
.round_rate = &tegra2_emc_clk_round_rate,
|
||||
.reset = &tegra2_periph_clk_reset,
|
||||
};
|
||||
|
||||
/* Clock doubler ops */
|
||||
static void tegra2_clk_double_init(struct clk *c)
|
||||
{
|
||||
|
@ -1948,6 +1998,18 @@ static struct clk_mux_sel mux_pclk[] = {
|
|||
{ 0, 0},
|
||||
};
|
||||
|
||||
static struct clk tegra_clk_emc = {
|
||||
.name = "emc",
|
||||
.ops = &tegra_emc_clk_ops,
|
||||
.reg = 0x19c,
|
||||
.max_rate = 800000000,
|
||||
.inputs = mux_pllm_pllc_pllp_clkm,
|
||||
.flags = MUX | DIV_U71 | PERIPH_EMC_ENB,
|
||||
.u.periph = {
|
||||
.clk_num = 57,
|
||||
},
|
||||
};
|
||||
|
||||
#define PERIPH_CLK(_name, _dev, _con, _clk_num, _reg, _max, _inputs, _flags) \
|
||||
{ \
|
||||
.name = _name, \
|
||||
|
@ -2039,7 +2101,6 @@ struct clk tegra_list_clks[] = {
|
|||
PERIPH_CLK("usbd", "fsl-tegra-udc", NULL, 22, 0, 480000000, mux_clk_m, 0), /* requires min voltage */
|
||||
PERIPH_CLK("usb2", "tegra-ehci.1", NULL, 58, 0, 480000000, mux_clk_m, 0), /* requires min voltage */
|
||||
PERIPH_CLK("usb3", "tegra-ehci.2", NULL, 59, 0, 480000000, mux_clk_m, 0), /* requires min voltage */
|
||||
PERIPH_CLK("emc", "emc", NULL, 57, 0x19c, 800000000, mux_pllm_pllc_pllp_clkm, MUX | DIV_U71 | PERIPH_EMC_ENB),
|
||||
PERIPH_CLK("dsi", "dsi", NULL, 48, 0, 500000000, mux_plld, 0), /* scales with voltage */
|
||||
PERIPH_CLK("csi", "tegra_camera", "csi", 52, 0, 72000000, mux_pllp_out3, 0),
|
||||
PERIPH_CLK("isp", "tegra_camera", "isp", 23, 0, 150000000, mux_clk_m, 0), /* same frequency as VI */
|
||||
|
@ -2115,6 +2176,7 @@ struct clk *tegra_ptr_clks[] = {
|
|||
&tegra_dev2_clk,
|
||||
&tegra_clk_virtual_cpu,
|
||||
&tegra_clk_blink,
|
||||
&tegra_clk_emc,
|
||||
};
|
||||
|
||||
static void tegra2_init_one_clock(struct clk *c)
|
||||
|
|
Loading…
Reference in a new issue