Commit 9f55b17f authored by Geert Uytterhoeven's avatar Geert Uytterhoeven

clk: renesas: rcar-gen3: Restore SDHI clocks during resume

On R-Car Gen3 systems, PSCI system suspend powers down the SoC, losing
clock configuration.  Register a notifier to save/restore SDHI clock
registers during system suspend/resume.

This is implemented using the cpg_simple_notifier abstraction, which can
be reused for others clocks that just need to save/restore a single
register.
Signed-off-by: default avatarGeert Uytterhoeven <geert+renesas@glider.be>
Tested-by: default avatarNiklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
parent 9f8c71e5
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/pm.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/sys_soc.h> #include <linux/sys_soc.h>
...@@ -29,6 +30,36 @@ ...@@ -29,6 +30,36 @@
#define CPG_PLL2CR 0x002c #define CPG_PLL2CR 0x002c
#define CPG_PLL4CR 0x01f4 #define CPG_PLL4CR 0x01f4
struct cpg_simple_notifier {
struct notifier_block nb;
void __iomem *reg;
u32 saved;
};
static int cpg_simple_notifier_call(struct notifier_block *nb,
unsigned long action, void *data)
{
struct cpg_simple_notifier *csn =
container_of(nb, struct cpg_simple_notifier, nb);
switch (action) {
case PM_EVENT_SUSPEND:
csn->saved = readl(csn->reg);
return NOTIFY_OK;
case PM_EVENT_RESUME:
writel(csn->saved, csn->reg);
return NOTIFY_OK;
}
return NOTIFY_DONE;
}
static void cpg_simple_notifier_register(struct raw_notifier_head *notifiers,
struct cpg_simple_notifier *csn)
{
csn->nb.notifier_call = cpg_simple_notifier_call;
raw_notifier_chain_register(notifiers, &csn->nb);
}
/* /*
* SDn Clock * SDn Clock
...@@ -55,8 +86,8 @@ struct sd_div_table { ...@@ -55,8 +86,8 @@ struct sd_div_table {
struct sd_clock { struct sd_clock {
struct clk_hw hw; struct clk_hw hw;
void __iomem *reg;
const struct sd_div_table *div_table; const struct sd_div_table *div_table;
struct cpg_simple_notifier csn;
unsigned int div_num; unsigned int div_num;
unsigned int div_min; unsigned int div_min;
unsigned int div_max; unsigned int div_max;
...@@ -97,12 +128,12 @@ static const struct sd_div_table cpg_sd_div_table[] = { ...@@ -97,12 +128,12 @@ static const struct sd_div_table cpg_sd_div_table[] = {
static int cpg_sd_clock_enable(struct clk_hw *hw) static int cpg_sd_clock_enable(struct clk_hw *hw)
{ {
struct sd_clock *clock = to_sd_clock(hw); struct sd_clock *clock = to_sd_clock(hw);
u32 val = readl(clock->reg); u32 val = readl(clock->csn.reg);
val &= ~(CPG_SD_STP_MASK); val &= ~(CPG_SD_STP_MASK);
val |= clock->div_table[clock->cur_div_idx].val & CPG_SD_STP_MASK; val |= clock->div_table[clock->cur_div_idx].val & CPG_SD_STP_MASK;
writel(val, clock->reg); writel(val, clock->csn.reg);
return 0; return 0;
} }
...@@ -111,14 +142,14 @@ static void cpg_sd_clock_disable(struct clk_hw *hw) ...@@ -111,14 +142,14 @@ static void cpg_sd_clock_disable(struct clk_hw *hw)
{ {
struct sd_clock *clock = to_sd_clock(hw); struct sd_clock *clock = to_sd_clock(hw);
writel(readl(clock->reg) | CPG_SD_STP_MASK, clock->reg); writel(readl(clock->csn.reg) | CPG_SD_STP_MASK, clock->csn.reg);
} }
static int cpg_sd_clock_is_enabled(struct clk_hw *hw) static int cpg_sd_clock_is_enabled(struct clk_hw *hw)
{ {
struct sd_clock *clock = to_sd_clock(hw); struct sd_clock *clock = to_sd_clock(hw);
return !(readl(clock->reg) & CPG_SD_STP_MASK); return !(readl(clock->csn.reg) & CPG_SD_STP_MASK);
} }
static unsigned long cpg_sd_clock_recalc_rate(struct clk_hw *hw, static unsigned long cpg_sd_clock_recalc_rate(struct clk_hw *hw,
...@@ -170,10 +201,10 @@ static int cpg_sd_clock_set_rate(struct clk_hw *hw, unsigned long rate, ...@@ -170,10 +201,10 @@ static int cpg_sd_clock_set_rate(struct clk_hw *hw, unsigned long rate,
clock->cur_div_idx = i; clock->cur_div_idx = i;
val = readl(clock->reg); val = readl(clock->csn.reg);
val &= ~(CPG_SD_STP_MASK | CPG_SD_FC_MASK); val &= ~(CPG_SD_STP_MASK | CPG_SD_FC_MASK);
val |= clock->div_table[i].val & (CPG_SD_STP_MASK | CPG_SD_FC_MASK); val |= clock->div_table[i].val & (CPG_SD_STP_MASK | CPG_SD_FC_MASK);
writel(val, clock->reg); writel(val, clock->csn.reg);
return 0; return 0;
} }
...@@ -188,8 +219,8 @@ static const struct clk_ops cpg_sd_clock_ops = { ...@@ -188,8 +219,8 @@ static const struct clk_ops cpg_sd_clock_ops = {
}; };
static struct clk * __init cpg_sd_clk_register(const struct cpg_core_clk *core, static struct clk * __init cpg_sd_clk_register(const struct cpg_core_clk *core,
void __iomem *base, void __iomem *base, const char *parent_name,
const char *parent_name) struct raw_notifier_head *notifiers)
{ {
struct clk_init_data init; struct clk_init_data init;
struct sd_clock *clock; struct sd_clock *clock;
...@@ -207,12 +238,12 @@ static struct clk * __init cpg_sd_clk_register(const struct cpg_core_clk *core, ...@@ -207,12 +238,12 @@ static struct clk * __init cpg_sd_clk_register(const struct cpg_core_clk *core,
init.parent_names = &parent_name; init.parent_names = &parent_name;
init.num_parents = 1; init.num_parents = 1;
clock->reg = base + core->offset; clock->csn.reg = base + core->offset;
clock->hw.init = &init; clock->hw.init = &init;
clock->div_table = cpg_sd_div_table; clock->div_table = cpg_sd_div_table;
clock->div_num = ARRAY_SIZE(cpg_sd_div_table); clock->div_num = ARRAY_SIZE(cpg_sd_div_table);
sd_fc = readl(clock->reg) & CPG_SD_FC_MASK; sd_fc = readl(clock->csn.reg) & CPG_SD_FC_MASK;
for (i = 0; i < clock->div_num; i++) for (i = 0; i < clock->div_num; i++)
if (sd_fc == (clock->div_table[i].val & CPG_SD_FC_MASK)) if (sd_fc == (clock->div_table[i].val & CPG_SD_FC_MASK))
break; break;
...@@ -233,8 +264,13 @@ static struct clk * __init cpg_sd_clk_register(const struct cpg_core_clk *core, ...@@ -233,8 +264,13 @@ static struct clk * __init cpg_sd_clk_register(const struct cpg_core_clk *core,
clk = clk_register(NULL, &clock->hw); clk = clk_register(NULL, &clock->hw);
if (IS_ERR(clk)) if (IS_ERR(clk))
kfree(clock); goto free_clock;
cpg_simple_notifier_register(notifiers, &clock->csn);
return clk;
free_clock:
kfree(clock);
return clk; return clk;
} }
...@@ -332,7 +368,8 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev, ...@@ -332,7 +368,8 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev,
break; break;
case CLK_TYPE_GEN3_SD: case CLK_TYPE_GEN3_SD:
return cpg_sd_clk_register(core, base, __clk_get_name(parent)); return cpg_sd_clk_register(core, base, __clk_get_name(parent),
notifiers);
case CLK_TYPE_GEN3_R: case CLK_TYPE_GEN3_R:
if (cpg_quirks & RCKCR_CKSEL) { if (cpg_quirks & RCKCR_CKSEL) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment