diff options
-rw-r--r-- | arch/arm/mach-tegra/clock.h | 4 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra3_clocks.c | 100 |
2 files changed, 80 insertions, 24 deletions
diff --git a/arch/arm/mach-tegra/clock.h b/arch/arm/mach-tegra/clock.h index 25ba88bc4893..2bb0fa76a8c3 100644 --- a/arch/arm/mach-tegra/clock.h +++ b/arch/arm/mach-tegra/clock.h @@ -176,9 +176,13 @@ struct clk { struct { struct clk *main; struct clk *backup; + unsigned long backup_rate; enum cpu_mode mode; } cpu; struct { + u32 div71; + } cclk; + struct { struct clk *pclk; struct clk *hclk; struct clk *sclk_low; diff --git a/arch/arm/mach-tegra/tegra3_clocks.c b/arch/arm/mach-tegra/tegra3_clocks.c index e0b0c7389385..5186d64cfdd8 100644 --- a/arch/arm/mach-tegra/tegra3_clocks.c +++ b/arch/arm/mach-tegra/tegra3_clocks.c @@ -210,8 +210,6 @@ #define SUPER_CLOCK_SKIP_ENABLE (0x1 << 31) #define SUPER_CLOCK_DIV_U71_SHIFT 16 #define SUPER_CLOCK_DIV_U71_MASK (0xff << SUPER_CLOCK_DIV_U71_SHIFT) -/* guarantees safe cpu backup */ -#define SUPER_CLOCK_DIV_U71_MIN 0x2 #define SUPER_CLOCK_SKIP_NOMIN_SHIFT 8 #define SUPER_CLOCK_SKIP_DENOM_SHIFT 0 #define SUPER_CLOCK_SKIP_MASK (0xffff << SUPER_CLOCK_SKIP_DENOM_SHIFT) @@ -303,6 +301,37 @@ #define ROUND_DIVIDER_UP 0 #define ROUND_DIVIDER_DOWN 1 +#ifdef CONFIG_TEGRA_SILICON_PLATFORM +#define PLLP_FIXED_RATE 408000000 +#else +#define PLLP_FIXED_RATE 216000000 +#endif + +/* sbus threshold must be exact factor of pll_p */ +#define SBUS_THRESHOLD_RATE (PLLP_FIXED_RATE / 2) + +/* + * Backup rate targets for each CPU mode is selected below Fmax(Vmin), and + * high enough to avoid voltage droop when CPU clock is switched between + * backup and main clock sources. Actual backup rates will be rounded based + * on backup source fixed frequency. Maximum stay-on-backup rate will be set + * as a minimum of G and LP backup rates to be supported in both modes. + */ +#define CPU_G_BACKUP_RATE_TARGET 440000000 +#define CPU_G_BACKUP_RATE_DIV \ + DIV_ROUND_UP(PLLP_FIXED_RATE, CPU_G_BACKUP_RATE_TARGET) +#define CPU_G_BACKUP_RATE \ + (PLLP_FIXED_RATE / CPU_G_BACKUP_RATE_DIV) + +#define CPU_LP_BACKUP_RATE_TARGET 220000000 +#define CPU_LP_BACKUP_RATE_DIV \ + DIV_ROUND_UP(PLLP_FIXED_RATE, CPU_LP_BACKUP_RATE_TARGET) +#define CPU_LP_BACKUP_RATE \ + (PLLP_FIXED_RATE / CPU_LP_BACKUP_RATE_DIV) + +#define CPU_STAY_ON_BACKUP_MAX \ + min(CPU_G_BACKUP_RATE, CPU_LP_BACKUP_RATE) + static bool tegra3_clk_is_parent_allowed(struct clk *c, struct clk *p); static int tegra3_clk_shared_bus_update(struct clk *bus); @@ -611,13 +640,19 @@ static void tegra3_super_clk_init(struct clk *c) /* Init safe 7.1 divider value (does not affect PLLX path). Super skipper is enabled to be ready for emergency throttle, but set 1:1 */ - val = SUPER_CLOCK_SKIP_ENABLE | - (SUPER_CLOCK_DIV_U71_MIN << SUPER_CLOCK_DIV_U71_SHIFT); - clk_writel(val, c->reg + SUPER_CLK_DIVIDER); c->mul = 2; c->div = 2; - if (!(c->parent->flags & PLLX)) - c->div += SUPER_CLOCK_DIV_U71_MIN; + if (!(c->parent->flags & PLLX)) { + val = clk_readl(c->reg + SUPER_CLK_DIVIDER); + val &= SUPER_CLOCK_DIV_U71_MASK; + val >>= SUPER_CLOCK_DIV_U71_SHIFT; + val = max(val, c->u.cclk.div71); + c->u.cclk.div71 = val; + c->div += val; + } + val = SUPER_CLOCK_SKIP_ENABLE + + (c->u.cclk.div71 << SUPER_CLOCK_DIV_U71_SHIFT); + clk_writel(val, c->reg + SUPER_CLK_DIVIDER); } else clk_writel(0, c->reg + SUPER_CLK_DIVIDER); @@ -734,8 +769,11 @@ static int tegra3_super_clk_set_rate(struct clk *c, unsigned long rate) if ((c->flags & DIV_U71) && (c->parent->flags & PLL_FIXED)) { int div = clk_div71_get_divider(c->parent->u.pll.fixed_rate, rate, c->flags, ROUND_DIVIDER_DOWN); - div = max(div, SUPER_CLOCK_DIV_U71_MIN); + if (div < 0) + return div; + tegra3_super_clk_divider_update(c, div); + c->u.cclk.div71 = div; c->div = div + 2; c->mul = 2; return 0; @@ -823,15 +861,29 @@ static int tegra3_cpu_clk_set_rate(struct clk *c, unsigned long rate) */ clk_enable(c->u.cpu.main); - ret = clk_set_parent(c->parent, c->u.cpu.backup); - if (ret) { - pr_err("Failed to switch cpu to clock %s\n", c->u.cpu.backup->name); - goto out; + if (c->parent->parent != c->u.cpu.backup) { + ret = clk_set_parent(c->parent, c->u.cpu.backup); + if (ret) { + pr_err("Failed to switch cpu to clock %s\n", + c->u.cpu.backup->name); + goto out; + } } - ret = clk_set_rate(c->parent, rate); - if (!ret && (rate <= clk_get_rate(c->parent))) + if (rate <= CPU_STAY_ON_BACKUP_MAX) { + ret = clk_set_rate(c->parent, rate); + if (ret) + pr_err("Failed to set cpu rate %lu on backup source\n", + rate); goto out; + } else { + ret = clk_set_rate(c->parent, c->u.cpu.backup_rate); + if (ret) { + pr_err("Failed to set cpu rate %lu on backup source\n", + c->u.cpu.backup_rate); + goto out; + } + } if (rate != clk_get_rate(c->u.cpu.main)) { ret = clk_set_rate(c->u.cpu.main, rate); @@ -3167,11 +3219,7 @@ static struct clk tegra_pll_p = { .vco_max = 1400000000, .freq_table = tegra_pll_p_freq_table, .lock_delay = 300, -#ifdef CONFIG_TEGRA_SILICON_PLATFORM - .fixed_rate = 408000000, -#else - .fixed_rate = 216000000, -#endif + .fixed_rate = PLLP_FIXED_RATE, }, }; @@ -3710,6 +3758,9 @@ static struct clk tegra_clk_cclk_g = { .reg = 0x368, .ops = &tegra_super_ops, .max_rate = 1700000000, + .u.cclk = { + .div71 = 2 * CPU_G_BACKUP_RATE_DIV - 2, + }, }; static struct clk tegra_clk_cclk_lp = { @@ -3719,6 +3770,9 @@ static struct clk tegra_clk_cclk_lp = { .reg = 0x370, .ops = &tegra_super_ops, .max_rate = 620000000, + .u.cclk = { + .div71 = 2 * CPU_LP_BACKUP_RATE_DIV - 2, + }, }; static struct clk tegra_clk_sclk = { @@ -3738,6 +3792,7 @@ static struct clk tegra_clk_virtual_cpu_g = { .u.cpu = { .main = &tegra_pll_x, .backup = &tegra_pll_p, + .backup_rate = CPU_G_BACKUP_RATE, .mode = MODE_G, }, }; @@ -3750,6 +3805,7 @@ static struct clk tegra_clk_virtual_cpu_lp = { .u.cpu = { .main = &tegra_pll_x, .backup = &tegra_pll_p, + .backup_rate = CPU_LP_BACKUP_RATE, .mode = MODE_LP, }, }; @@ -3807,11 +3863,7 @@ static struct clk tegra_clk_sbus_cmplx = { .hclk = &tegra_clk_hclk, .sclk_low = &tegra_pll_p_out4, .sclk_high = &tegra_pll_m_out1, -#ifdef CONFIG_TEGRA_SILICON_PLATFORM - .threshold = 204000000, /* exact factor of low range pll_p */ -#else - .threshold = 108000000, /* exact factor of low range pll_p */ -#endif + .threshold = SBUS_THRESHOLD_RATE, /* exact factor of pll_p */ }, .rate_change_nh = &sbus_rate_change_nh, }; |