clk: renesas: rcar-gen3: Restore SDHI clocks during resume
authorGeert Uytterhoeven <geert+renesas@glider.be>
Wed, 21 Jun 2017 20:51:21 +0000 (22:51 +0200)
committerGeert Uytterhoeven <geert+renesas@glider.be>
Fri, 20 Oct 2017 09:16:05 +0000 (11:16 +0200)
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: Geert Uytterhoeven <geert+renesas@glider.be>
Tested-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
drivers/clk/renesas/rcar-gen3-cpg.c

index 1399852..267b562 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/err.h>
 #include <linux/init.h>
 #include <linux/io.h>
+#include <linux/pm.h>
 #include <linux/slab.h>
 #include <linux/sys_soc.h>
 
 #define CPG_PLL2CR             0x002c
 #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
@@ -55,8 +86,8 @@ struct sd_div_table {
 
 struct sd_clock {
        struct clk_hw hw;
-       void __iomem *reg;
        const struct sd_div_table *div_table;
+       struct cpg_simple_notifier csn;
        unsigned int div_num;
        unsigned int div_min;
        unsigned int div_max;
@@ -97,12 +128,12 @@ static const struct sd_div_table cpg_sd_div_table[] = {
 static int cpg_sd_clock_enable(struct clk_hw *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 |= clock->div_table[clock->cur_div_idx].val & CPG_SD_STP_MASK;
 
-       writel(val, clock->reg);
+       writel(val, clock->csn.reg);
 
        return 0;
 }
@@ -111,14 +142,14 @@ static void cpg_sd_clock_disable(struct clk_hw *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)
 {
        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,
@@ -170,10 +201,10 @@ static int cpg_sd_clock_set_rate(struct clk_hw *hw, unsigned long rate,
 
        clock->cur_div_idx = i;
 
-       val = readl(clock->reg);
+       val = readl(clock->csn.reg);
        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;
 }
@@ -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,
-                                              void __iomem *base,
-                                              const char *parent_name)
+       void __iomem *base, const char *parent_name,
+       struct raw_notifier_head *notifiers)
 {
        struct clk_init_data init;
        struct sd_clock *clock;
@@ -207,12 +238,12 @@ static struct clk * __init cpg_sd_clk_register(const struct cpg_core_clk *core,
        init.parent_names = &parent_name;
        init.num_parents = 1;
 
-       clock->reg = base + core->offset;
+       clock->csn.reg = base + core->offset;
        clock->hw.init = &init;
        clock->div_table = 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++)
                if (sd_fc == (clock->div_table[i].val & CPG_SD_FC_MASK))
                        break;
@@ -233,8 +264,13 @@ static struct clk * __init cpg_sd_clk_register(const struct cpg_core_clk *core,
 
        clk = clk_register(NULL, &clock->hw);
        if (IS_ERR(clk))
-               kfree(clock);
+               goto free_clock;
+
+       cpg_simple_notifier_register(notifiers, &clock->csn);
+       return clk;
 
+free_clock:
+       kfree(clock);
        return clk;
 }
 
@@ -332,7 +368,8 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev,
                break;
 
        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:
                if (cpg_quirks & RCKCR_CKSEL) {