Skip to content

Commit 535f296

Browse files
Sowjanya Komatinenithierryreding
authored andcommitted
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 <[email protected]> Reviewed-by: Dmitry Osipenko <[email protected]> Signed-off-by: Thierry Reding <[email protected]>
1 parent 3214be6 commit 535f296

File tree

3 files changed

+163
-4
lines changed

3 files changed

+163
-4
lines changed

drivers/clk/tegra/clk-tegra210.c

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@
99
#include <linux/clkdev.h>
1010
#include <linux/of.h>
1111
#include <linux/of_address.h>
12+
#include <linux/syscore_ops.h>
1213
#include <linux/delay.h>
1314
#include <linux/export.h>
1415
#include <linux/mutex.h>
1516
#include <linux/clk/tegra.h>
1617
#include <dt-bindings/clock/tegra210-car.h>
1718
#include <dt-bindings/reset/tegra210-car.h>
18-
#include <linux/iopoll.h>
1919
#include <linux/sizes.h>
2020
#include <soc/tegra/pmc.h>
2121

@@ -221,11 +221,15 @@
221221
#define CLK_M_DIVISOR_SHIFT 2
222222
#define CLK_M_DIVISOR_MASK 0x3
223223

224+
#define CLK_MASK_ARM 0x44
225+
#define MISC_CLK_ENB 0x48
226+
224227
#define RST_DFLL_DVCO 0x2f4
225228
#define DVFS_DFLL_RESET_SHIFT 0
226229

227230
#define CLK_RST_CONTROLLER_RST_DEV_Y_SET 0x2a8
228231
#define CLK_RST_CONTROLLER_RST_DEV_Y_CLR 0x2ac
232+
#define CPU_SOFTRST_CTRL 0x380
229233

230234
#define LVL2_CLK_GATE_OVRA 0xf8
231235
#define LVL2_CLK_GATE_OVRC 0x3a0
@@ -2826,6 +2830,7 @@ static int tegra210_enable_pllu(void)
28262830
struct tegra_clk_pll_freq_table *fentry;
28272831
struct tegra_clk_pll pllu;
28282832
u32 reg;
2833+
int ret;
28292834

28302835
for (fentry = pll_u_freq_table; fentry->input_rate; fentry++) {
28312836
if (fentry->input_rate == pll_ref_freq)
@@ -2854,9 +2859,14 @@ static int tegra210_enable_pllu(void)
28542859
reg |= PLL_ENABLE;
28552860
writel(reg, clk_base + PLLU_BASE);
28562861

2857-
readl_relaxed_poll_timeout_atomic(clk_base + PLLU_BASE, reg,
2858-
reg & PLL_BASE_LOCK, 2, 1000);
2859-
if (!(reg & PLL_BASE_LOCK)) {
2862+
/*
2863+
* During clocks resume, same PLLU init and enable sequence get
2864+
* executed. So, readx_poll_timeout_atomic can't be used here as it
2865+
* uses ktime_get() and timekeeping resume doesn't happen by that
2866+
* time. So, using tegra210_wait_for_mask for PLL LOCK.
2867+
*/
2868+
ret = tegra210_wait_for_mask(&pllu, PLLU_BASE, PLL_BASE_LOCK);
2869+
if (ret) {
28602870
pr_err("Timed out waiting for PLL_U to lock\n");
28612871
return -ETIMEDOUT;
28622872
}
@@ -3326,6 +3336,77 @@ static void tegra210_disable_cpu_clock(u32 cpu)
33263336
}
33273337

33283338
#ifdef CONFIG_PM_SLEEP
3339+
#define car_readl(_base, _off) readl_relaxed(clk_base + (_base) + ((_off) * 4))
3340+
#define car_writel(_val, _base, _off) \
3341+
writel_relaxed(_val, clk_base + (_base) + ((_off) * 4))
3342+
3343+
static u32 spare_reg_ctx, misc_clk_enb_ctx, clk_msk_arm_ctx;
3344+
static u32 cpu_softrst_ctx[3];
3345+
3346+
static int tegra210_clk_suspend(void)
3347+
{
3348+
unsigned int i;
3349+
3350+
clk_save_context();
3351+
3352+
/*
3353+
* Save the bootloader configured clock registers SPARE_REG0,
3354+
* MISC_CLK_ENB, CLK_MASK_ARM, CPU_SOFTRST_CTRL.
3355+
*/
3356+
spare_reg_ctx = readl_relaxed(clk_base + SPARE_REG0);
3357+
misc_clk_enb_ctx = readl_relaxed(clk_base + MISC_CLK_ENB);
3358+
clk_msk_arm_ctx = readl_relaxed(clk_base + CLK_MASK_ARM);
3359+
3360+
for (i = 0; i < ARRAY_SIZE(cpu_softrst_ctx); i++)
3361+
cpu_softrst_ctx[i] = car_readl(CPU_SOFTRST_CTRL, i);
3362+
3363+
tegra_clk_periph_suspend();
3364+
return 0;
3365+
}
3366+
3367+
static void tegra210_clk_resume(void)
3368+
{
3369+
unsigned int i;
3370+
3371+
tegra_clk_osc_resume(clk_base);
3372+
3373+
/*
3374+
* Restore the bootloader configured clock registers SPARE_REG0,
3375+
* MISC_CLK_ENB, CLK_MASK_ARM, CPU_SOFTRST_CTRL from saved context.
3376+
*/
3377+
writel_relaxed(spare_reg_ctx, clk_base + SPARE_REG0);
3378+
writel_relaxed(misc_clk_enb_ctx, clk_base + MISC_CLK_ENB);
3379+
writel_relaxed(clk_msk_arm_ctx, clk_base + CLK_MASK_ARM);
3380+
3381+
for (i = 0; i < ARRAY_SIZE(cpu_softrst_ctx); i++)
3382+
car_writel(cpu_softrst_ctx[i], CPU_SOFTRST_CTRL, i);
3383+
3384+
/*
3385+
* Tegra clock programming sequence recommends peripheral clock to
3386+
* be enabled prior to changing its clock source and divider to
3387+
* prevent glitchless frequency switch.
3388+
* So, enable all peripheral clocks before restoring their source
3389+
* and dividers.
3390+
*/
3391+
writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_L, clk_base + CLK_OUT_ENB_L);
3392+
writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_H, clk_base + CLK_OUT_ENB_H);
3393+
writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_U, clk_base + CLK_OUT_ENB_U);
3394+
writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_V, clk_base + CLK_OUT_ENB_V);
3395+
writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_W, clk_base + CLK_OUT_ENB_W);
3396+
writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_X, clk_base + CLK_OUT_ENB_X);
3397+
writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_Y, clk_base + CLK_OUT_ENB_Y);
3398+
3399+
/* wait for all writes to happen to have all the clocks enabled */
3400+
fence_udelay(2, clk_base);
3401+
3402+
/* restore PLLs and all peripheral clock rates */
3403+
tegra210_init_pllu();
3404+
clk_restore_context();
3405+
3406+
/* restore saved context of peripheral clocks and reset state */
3407+
tegra_clk_periph_resume();
3408+
}
3409+
33293410
static void tegra210_cpu_clock_suspend(void)
33303411
{
33313412
/* switch coresite to clk_m, save off original source */
@@ -3341,6 +3422,11 @@ static void tegra210_cpu_clock_resume(void)
33413422
}
33423423
#endif
33433424

3425+
static struct syscore_ops tegra_clk_syscore_ops = {
3426+
.suspend = tegra210_clk_suspend,
3427+
.resume = tegra210_clk_resume,
3428+
};
3429+
33443430
static struct tegra_cpu_car_ops tegra210_cpu_car_ops = {
33453431
.wait_for_reset = tegra210_wait_cpu_in_reset,
33463432
.disable_clock = tegra210_disable_cpu_clock,
@@ -3625,5 +3711,7 @@ static void __init tegra210_clock_init(struct device_node *np)
36253711
tegra210_mbist_clk_init();
36263712

36273713
tegra_cpu_car_ops = &tegra210_cpu_car_ops;
3714+
3715+
register_syscore_ops(&tegra_clk_syscore_ops);
36283716
}
36293717
CLK_OF_DECLARE(tegra210, "nvidia,tegra210-car", tegra210_clock_init);

drivers/clk/tegra/clk.c

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ struct tegra_cpu_car_ops *tegra_cpu_car_ops = &dummy_car_ops;
2222

2323
int *periph_clk_enb_refcnt;
2424
static int periph_banks;
25+
static u32 *periph_state_ctx;
2526
static struct clk **clks;
2627
static int clk_num;
2728
static struct clk_onecell_data clk_data;
@@ -168,6 +169,52 @@ void tegra_clk_set_pllp_out_cpu(bool enable)
168169
writel_relaxed(val, clk_base + CLK_OUT_ENB_Y);
169170
}
170171

172+
void tegra_clk_periph_suspend(void)
173+
{
174+
unsigned int i, idx;
175+
176+
idx = 0;
177+
for (i = 0; i < periph_banks; i++, idx++)
178+
periph_state_ctx[idx] =
179+
readl_relaxed(clk_base + periph_regs[i].enb_reg);
180+
181+
for (i = 0; i < periph_banks; i++, idx++)
182+
periph_state_ctx[idx] =
183+
readl_relaxed(clk_base + periph_regs[i].rst_reg);
184+
}
185+
186+
void tegra_clk_periph_resume(void)
187+
{
188+
unsigned int i, idx;
189+
190+
idx = 0;
191+
for (i = 0; i < periph_banks; i++, idx++)
192+
writel_relaxed(periph_state_ctx[idx],
193+
clk_base + periph_regs[i].enb_reg);
194+
/*
195+
* All non-boot peripherals will be in reset state on resume.
196+
* Wait for 5us of reset propagation delay before de-asserting
197+
* the peripherals based on the saved context.
198+
*/
199+
fence_udelay(5, clk_base);
200+
201+
for (i = 0; i < periph_banks; i++, idx++)
202+
writel_relaxed(periph_state_ctx[idx],
203+
clk_base + periph_regs[i].rst_reg);
204+
205+
fence_udelay(2, clk_base);
206+
}
207+
208+
static int tegra_clk_periph_ctx_init(int banks)
209+
{
210+
periph_state_ctx = kcalloc(2 * banks, sizeof(*periph_state_ctx),
211+
GFP_KERNEL);
212+
if (!periph_state_ctx)
213+
return -ENOMEM;
214+
215+
return 0;
216+
}
217+
171218
struct clk ** __init tegra_clk_init(void __iomem *regs, int num, int banks)
172219
{
173220
clk_base = regs;
@@ -189,6 +236,14 @@ struct clk ** __init tegra_clk_init(void __iomem *regs, int num, int banks)
189236

190237
clk_num = num;
191238

239+
if (IS_ENABLED(CONFIG_PM_SLEEP)) {
240+
if (tegra_clk_periph_ctx_init(banks)) {
241+
kfree(periph_clk_enb_refcnt);
242+
kfree(clks);
243+
return NULL;
244+
}
245+
}
246+
192247
return clks;
193248
}
194249

drivers/clk/tegra/clk.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,20 @@
5555
#define RST_DEVICES_SET_Y 0x2a8
5656
#define RST_DEVICES_CLR_Y 0x2ac
5757

58+
/*
59+
* Tegra CLK_OUT_ENB registers have some undefined bits which are not used and
60+
* any accidental write of 1 to these bits can cause PSLVERR.
61+
* So below are the valid mask defines for each CLK_OUT_ENB register used to
62+
* turn ON only the valid clocks.
63+
*/
64+
#define TEGRA210_CLK_ENB_VLD_MSK_L 0xdcd7dff9
65+
#define TEGRA210_CLK_ENB_VLD_MSK_H 0x87d1f3e7
66+
#define TEGRA210_CLK_ENB_VLD_MSK_U 0xf3fed3fa
67+
#define TEGRA210_CLK_ENB_VLD_MSK_V 0xffc18cfb
68+
#define TEGRA210_CLK_ENB_VLD_MSK_W 0x793fb7ff
69+
#define TEGRA210_CLK_ENB_VLD_MSK_X 0x3fe66fff
70+
#define TEGRA210_CLK_ENB_VLD_MSK_Y 0xfc1fc7ff
71+
5872
/**
5973
* struct tegra_clk_sync_source - external clock source from codec
6074
*
@@ -880,6 +894,8 @@ int div_frac_get(unsigned long rate, unsigned parent_rate, u8 width,
880894
u8 frac_width, u8 flags);
881895
void tegra_clk_osc_resume(void __iomem *clk_base);
882896
void tegra_clk_set_pllp_out_cpu(bool enable);
897+
void tegra_clk_periph_suspend(void);
898+
void tegra_clk_periph_resume(void);
883899

884900

885901
/* Combined read fence with delay */

0 commit comments

Comments
 (0)