diff options
author | Sowjanya Komatineni <skomatineni@nvidia.com> | 2019-08-16 12:42:00 -0700 |
---|---|---|
committer | Thierry Reding <treding@nvidia.com> | 2019-11-11 14:53:04 +0100 |
commit | 535f296d47de327287fe65b5843713bd9b01a267 (patch) | |
tree | 73cabf151af02f59d0fb50a7e4848d04c080c0d6 /drivers/clk/tegra/clk.c | |
parent | 3214be6cb1e487b0f8c3bb2eac9b06df07a07e06 (diff) | |
download | linux-535f296d47de327287fe65b5843713bd9b01a267.tar.bz2 |
clk: tegra: Add suspend and resume support on Tegra210
All the CAR controller settings are lost on suspend when core power goes
off. This implement saving and restoring context for all PLLs and clocks
during system suspend and resume to have the clocks back to same state
for normal operation.
Clock driver suspend and resume are registered as syscore_ops as clocks
restore need to happen before the other drivers resume to have all their
clocks back to the same state as before suspend.
Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
Reviewed-by: Dmitry Osipenko <digetx@gmail.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
Diffstat (limited to 'drivers/clk/tegra/clk.c')
-rw-r--r-- | drivers/clk/tegra/clk.c | 55 |
1 files changed, 55 insertions, 0 deletions
diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c index 33ac88ee324a..e6bd6d1ea012 100644 --- a/drivers/clk/tegra/clk.c +++ b/drivers/clk/tegra/clk.c @@ -22,6 +22,7 @@ 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; @@ -168,6 +169,52 @@ void tegra_clk_set_pllp_out_cpu(bool enable) 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; @@ -189,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; } |