All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/5] clk: renesas: rcar-gen3: Restore clocks during resume
@ 2017-10-16  8:36 Geert Uytterhoeven
  2017-10-16  8:36 ` [PATCH v3 1/5] clk: renesas: cpg-mssr: Restore module " Geert Uytterhoeven
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Geert Uytterhoeven @ 2017-10-16  8:36 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd
  Cc: Niklas Söderlund, Laurent Pinchart, linux-renesas-soc,
	linux-clk, linux-pm, Geert Uytterhoeven

	Hi Mike, Stephen,

During PSCI system suspend, R-Car Gen3 SoCs are powered down, and their
clock register state is lost.  Note that as the boot loader skips most
initialization after resume, clock register state differs from the state
encountered during normal system boot, too.

Hence after s2ram, some operations may fail because module clocks are
disabled, while drivers expect them to be still enabled.  E.g. in the
absence of commit fbf3d034f2ff6264 ("ravb: add workaround for clock when
resuming with WoL enabled"), EtherAVB fails when Wake-on-LAN has been
enabled using "ethtool -s eth0 wol g":

    ravb e6800000.ethernet eth0: failed to switch device to config mode
    ravb e6800000.ethernet eth0: device will be stopped after h/w processes are done.
    ravb e6800000.ethernet eth0: failed to switch device to config
    PM: Device e6800000.ethernet failed to resume: error -110

In addition, some clocks that were disabled by clk_disable_unused() may
have been re-enabled, wasting power.

This patch series fixes these issues by restoring clock registers during
system resume.  Note that while this fixes EtherAVB operation after
resume from s2ram, EtherAVB cannot be used as an actual wake-up source
from s2ram, only from s2idle, due to PSCI limitations.

To avoid overhead on platforms not needing it, the suspend/resume code
has a build time dependency on sleep and PSCI support, and a runtime
dependency on PSCI.

Changes compared to v2:
  - Drop RFC state,
  - Add Tested-by,
  - Add build and runtime dependencies on PM_SLEEP and PSCI.

Changes compared to v1 (more details in the individual patches):
  - Save module clock registers in suspend_noirq instead of constantly
    updating shadow registers,
  - Restore all module clocks under our control, not just the ones we ever
    changed,
  - Also restore DIV6, SDHI, and R clocks, thus covering all supported
    programmable core clocks on R-Car Gen3.

This series is against the clk-renesas branch of my renesas-drivers git
repository at
git://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-drivers.git.
For your convenience, this series is also available in the
topic/cpg-mssr-suspend-resume-v3 branch of the same repository.

This has been tested on Salvator-X with R-Car H3 ES1.0 and M3-W ES1.0,
and on Salvator-XS with R-Car H3 ES2.0 (the latter also needs "[PATCH
0/4] ravb: Add PHY reset support",
https://www.spinics.net/lists/netdev/msg457308.html), and
regression-tested on various other Renesas arm32 boards not using PSCI.

I plan to queue this up in clk-renesas-for-v4.15.
Thanks!

Geert Uytterhoeven (5):
  clk: renesas: cpg-mssr: Restore module clocks during resume
  clk: renesas: cpg-mssr: Add support to restore core clocks during
    resume
  clk: renesas: div6: Restore clock state during resume
  clk: renesas: rcar-gen3: Restore SDHI clocks during resume
  clk: renesas: rcar-gen3: Restore R clock during resume

 drivers/clk/renesas/clk-div6.c         | 38 ++++++++++++-
 drivers/clk/renesas/clk-div6.h         |  3 +-
 drivers/clk/renesas/rcar-gen2-cpg.c    |  7 ++-
 drivers/clk/renesas/rcar-gen2-cpg.h    |  6 +--
 drivers/clk/renesas/rcar-gen3-cpg.c    | 79 +++++++++++++++++++++------
 drivers/clk/renesas/rcar-gen3-cpg.h    |  3 +-
 drivers/clk/renesas/renesas-cpg-mssr.c | 99 +++++++++++++++++++++++++++++++++-
 drivers/clk/renesas/renesas-cpg-mssr.h |  3 +-
 8 files changed, 208 insertions(+), 30 deletions(-)

-- 
2.7.4

Gr{oetje,eeting}s,

						Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
							    -- Linus Torvalds

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v3 1/5] clk: renesas: cpg-mssr: Restore module clocks during resume
  2017-10-16  8:36 [PATCH v3 0/5] clk: renesas: rcar-gen3: Restore clocks during resume Geert Uytterhoeven
@ 2017-10-16  8:36 ` Geert Uytterhoeven
  2017-10-16  8:36 ` [PATCH v3 2/5] clk: renesas: cpg-mssr: Add support to restore core " Geert Uytterhoeven
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Geert Uytterhoeven @ 2017-10-16  8:36 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd
  Cc: Niklas Söderlund, Laurent Pinchart, linux-renesas-soc,
	linux-clk, linux-pm, Geert Uytterhoeven

During PSCI system suspend, R-Car Gen3 SoCs are powered down, and their
clock register state is lost.  Note that as the boot loader skips most
initialization after system resume, clock register state differs from
the state encountered during normal system boot, too.

Hence after s2ram, some operations may fail because module clocks are
disabled, while drivers expect them to be still enabled.  E.g. EtherAVB
fails when Wake-on-LAN has been enabled using "ethtool -s eth0 wol g":

    ravb e6800000.ethernet eth0: failed to switch device to config mode
    ravb e6800000.ethernet eth0: device will be stopped after h/w processes are done.
    ravb e6800000.ethernet eth0: failed to switch device to config
    PM: Device e6800000.ethernet failed to resume: error -110

In addition, some module clocks that were disabled by
clk_disable_unused() may have been re-enabled, wasting power.

To fix this, restore all bits of the SMSTPCR registers that represent
clocks under control of Linux.

Notes:
  - While this fixes EtherAVB operation after resume from s2ram,
    EtherAVB cannot be used as an actual wake-up source from s2ram, only
    from s2idle, due to PSCI limitations,
  - To avoid overhead on platforms not needing it, the suspend/resume
    code has a build time dependency on sleep and PSCI support, and a
    runtime dependency on PSCI.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Tested-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
---
v3:
  - Drop RFC state,
  - Add Tested-by,
  - Add build and runtime dependencies on PM_SLEEP and PSCI,

v2:
  - Add Tested-by, Reviewed-by,
  - Mark cpg_mssr_resume_noirq __maybe_unused to kill warning if PM=n,
  - Move shadow register updates inside the RMW spinlock to protect
    against concurrent updates of multiple MSTP clocks in the same bank,
    just like for the actual registers,
  - Add more comments,
  - Save registers in suspend_noirq instead of constantly updating
    shadow registers
  - Let smstpcr_saved[].mask represent all clocks under our control, not
    just the ones we ever changed.  As clk_disable_unused() doesn't
    touch clocks that are not enabled, such clocks were not marked for
    restore, and thus may stay enabled after resume (resume state is
    different from boot state, as the boot loader disables some clocks
    during normal boot),
  - Drop Tested-by, Reviewed-by, as the code has changed substantially.
---
 drivers/clk/renesas/renesas-cpg-mssr.c | 84 ++++++++++++++++++++++++++++++++++
 1 file changed, 84 insertions(+)

diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c
index 1779b0cc7a2abe9d..15fc8679d3426f3e 100644
--- a/drivers/clk/renesas/renesas-cpg-mssr.c
+++ b/drivers/clk/renesas/renesas-cpg-mssr.c
@@ -26,6 +26,7 @@
 #include <linux/platform_device.h>
 #include <linux/pm_clock.h>
 #include <linux/pm_domain.h>
+#include <linux/psci.h>
 #include <linux/reset-controller.h>
 #include <linux/slab.h>
 
@@ -106,6 +107,8 @@ static const u16 srcr[] = {
  * @num_core_clks: Number of Core Clocks in clks[]
  * @num_mod_clks: Number of Module Clocks in clks[]
  * @last_dt_core_clk: ID of the last Core Clock exported to DT
+ * @smstpcr_saved[].mask: Mask of SMSTPCR[] bits under our control
+ * @smstpcr_saved[].val: Saved values of SMSTPCR[]
  */
 struct cpg_mssr_priv {
 #ifdef CONFIG_RESET_CONTROLLER
@@ -119,6 +122,11 @@ struct cpg_mssr_priv {
 	unsigned int num_core_clks;
 	unsigned int num_mod_clks;
 	unsigned int last_dt_core_clk;
+
+	struct {
+		u32 mask;
+		u32 val;
+	} smstpcr_saved[ARRAY_SIZE(smstpcr)];
 };
 
 
@@ -382,6 +390,7 @@ static void __init cpg_mssr_register_mod_clk(const struct mssr_mod_clk *mod,
 
 	dev_dbg(dev, "Module clock %pC at %pCr Hz\n", clk, clk);
 	priv->clks[id] = clk;
+	priv->smstpcr_saved[clock->index / 32].mask |= BIT(clock->index % 32);
 	return;
 
 fail:
@@ -700,6 +709,79 @@ static void cpg_mssr_del_clk_provider(void *data)
 	of_clk_del_provider(data);
 }
 
+#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_ARM_PSCI_FW)
+static int cpg_mssr_suspend_noirq(struct device *dev)
+{
+	struct cpg_mssr_priv *priv = dev_get_drvdata(dev);
+	unsigned int reg;
+
+	/* This is the best we can do to check for the presence of PSCI */
+	if (!psci_ops.cpu_suspend)
+		return 0;
+
+	/* Save module registers with bits under our control */
+	for (reg = 0; reg < ARRAY_SIZE(priv->smstpcr_saved); reg++) {
+		if (priv->smstpcr_saved[reg].mask)
+			priv->smstpcr_saved[reg].val =
+				readl(priv->base + SMSTPCR(reg));
+	}
+
+	return 0;
+}
+
+static int cpg_mssr_resume_noirq(struct device *dev)
+{
+	struct cpg_mssr_priv *priv = dev_get_drvdata(dev);
+	unsigned int reg, i;
+	u32 mask, oldval, newval;
+
+	/* This is the best we can do to check for the presence of PSCI */
+	if (!psci_ops.cpu_suspend)
+		return 0;
+
+	/* Restore module clocks */
+	for (reg = 0; reg < ARRAY_SIZE(priv->smstpcr_saved); reg++) {
+		mask = priv->smstpcr_saved[reg].mask;
+		if (!mask)
+			continue;
+
+		oldval = readl(priv->base + SMSTPCR(reg));
+		newval = oldval & ~mask;
+		newval |= priv->smstpcr_saved[reg].val & mask;
+		if (newval == oldval)
+			continue;
+
+		writel(newval, priv->base + SMSTPCR(reg));
+
+		/* Wait until enabled clocks are really enabled */
+		mask &= ~priv->smstpcr_saved[reg].val;
+		if (!mask)
+			continue;
+
+		for (i = 1000; i > 0; --i) {
+			oldval = readl(priv->base + MSTPSR(reg));
+			if (!(oldval & mask))
+				break;
+			cpu_relax();
+		}
+
+		if (!i)
+			dev_warn(dev, "Failed to enable SMSTP %p[0x%x]\n",
+				 priv->base + SMSTPCR(reg), oldval & mask);
+	}
+
+	return 0;
+}
+
+static const struct dev_pm_ops cpg_mssr_pm = {
+	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(cpg_mssr_suspend_noirq,
+				      cpg_mssr_resume_noirq)
+};
+#define DEV_PM_OPS	&cpg_mssr_pm
+#else
+#define DEV_PM_OPS	NULL
+#endif /* CONFIG_PM_SLEEP && CONFIG_ARM_PSCI_FW */
+
 static int __init cpg_mssr_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -735,6 +817,7 @@ static int __init cpg_mssr_probe(struct platform_device *pdev)
 	if (!clks)
 		return -ENOMEM;
 
+	dev_set_drvdata(dev, priv);
 	priv->clks = clks;
 	priv->num_core_clks = info->num_total_core_clks;
 	priv->num_mod_clks = info->num_hw_mod_clks;
@@ -775,6 +858,7 @@ static struct platform_driver cpg_mssr_driver = {
 	.driver		= {
 		.name	= "renesas-cpg-mssr",
 		.of_match_table = cpg_mssr_match,
+		.pm = DEV_PM_OPS,
 	},
 };
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v3 2/5] clk: renesas: cpg-mssr: Add support to restore core clocks during resume
  2017-10-16  8:36 [PATCH v3 0/5] clk: renesas: rcar-gen3: Restore clocks during resume Geert Uytterhoeven
  2017-10-16  8:36 ` [PATCH v3 1/5] clk: renesas: cpg-mssr: Restore module " Geert Uytterhoeven
@ 2017-10-16  8:36 ` Geert Uytterhoeven
  2017-10-16  8:36 ` [PATCH v3 3/5] clk: renesas: div6: Restore clock state " Geert Uytterhoeven
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Geert Uytterhoeven @ 2017-10-16  8:36 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd
  Cc: Niklas Söderlund, Laurent Pinchart, linux-renesas-soc,
	linux-clk, linux-pm, Geert Uytterhoeven

On R-Car Gen3 systems, PSCI system suspend powers down the SoC, possibly
losing clock configuration.  Hence add a notifier chain that can be used
by core clocks to save/restore clock state during system suspend/resume.

The implementation of the actual clock state save/restore operations is
clock-specific, and to be registered with the notifier chain in the SoC
or family-specific cpg_mssr_info.cpg_clk_register() callback.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Tested-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
---
v3:
  - Drop RFC state,
  - Add Tested-by,

v2:
  - New.
---
 drivers/clk/renesas/rcar-gen2-cpg.c    |  7 +++----
 drivers/clk/renesas/rcar-gen2-cpg.h    |  6 +++---
 drivers/clk/renesas/rcar-gen3-cpg.c    |  3 ++-
 drivers/clk/renesas/rcar-gen3-cpg.h    |  3 ++-
 drivers/clk/renesas/renesas-cpg-mssr.c | 12 +++++++++++-
 drivers/clk/renesas/renesas-cpg-mssr.h |  3 ++-
 6 files changed, 23 insertions(+), 11 deletions(-)

diff --git a/drivers/clk/renesas/rcar-gen2-cpg.c b/drivers/clk/renesas/rcar-gen2-cpg.c
index 123b1e622179308e..feb14579a71b3bf4 100644
--- a/drivers/clk/renesas/rcar-gen2-cpg.c
+++ b/drivers/clk/renesas/rcar-gen2-cpg.c
@@ -262,10 +262,9 @@ static unsigned int cpg_pll0_div __initdata;
 static u32 cpg_mode __initdata;
 
 struct clk * __init rcar_gen2_cpg_clk_register(struct device *dev,
-					       const struct cpg_core_clk *core,
-					       const struct cpg_mssr_info *info,
-					       struct clk **clks,
-					       void __iomem *base)
+	const struct cpg_core_clk *core, const struct cpg_mssr_info *info,
+	struct clk **clks, void __iomem *base,
+	struct raw_notifier_head *notifiers)
 {
 	const struct clk_div_table *table = NULL;
 	const struct clk *parent;
diff --git a/drivers/clk/renesas/rcar-gen2-cpg.h b/drivers/clk/renesas/rcar-gen2-cpg.h
index 9eba07ff8b11e20e..020a3baad0154231 100644
--- a/drivers/clk/renesas/rcar-gen2-cpg.h
+++ b/drivers/clk/renesas/rcar-gen2-cpg.h
@@ -34,9 +34,9 @@ struct rcar_gen2_cpg_pll_config {
 };
 
 struct clk *rcar_gen2_cpg_clk_register(struct device *dev,
-				       const struct cpg_core_clk *core,
-				       const struct cpg_mssr_info *info,
-				       struct clk **clks, void __iomem *base);
+	const struct cpg_core_clk *core, const struct cpg_mssr_info *info,
+	struct clk **clks, void __iomem *base,
+	struct raw_notifier_head *notifiers);
 int rcar_gen2_cpg_init(const struct rcar_gen2_cpg_pll_config *config,
 		       unsigned int pll0_div, u32 mode);
 
diff --git a/drivers/clk/renesas/rcar-gen3-cpg.c b/drivers/clk/renesas/rcar-gen3-cpg.c
index 9511058165475dd7..139985257003bfad 100644
--- a/drivers/clk/renesas/rcar-gen3-cpg.c
+++ b/drivers/clk/renesas/rcar-gen3-cpg.c
@@ -265,7 +265,8 @@ static const struct soc_device_attribute cpg_quirks_match[] __initconst = {
 
 struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev,
 	const struct cpg_core_clk *core, const struct cpg_mssr_info *info,
-	struct clk **clks, void __iomem *base)
+	struct clk **clks, void __iomem *base,
+	struct raw_notifier_head *notifiers)
 {
 	const struct clk *parent;
 	unsigned int mult = 1;
diff --git a/drivers/clk/renesas/rcar-gen3-cpg.h b/drivers/clk/renesas/rcar-gen3-cpg.h
index d756ef8b78eb6c02..2e4284399f530f96 100644
--- a/drivers/clk/renesas/rcar-gen3-cpg.h
+++ b/drivers/clk/renesas/rcar-gen3-cpg.h
@@ -44,7 +44,8 @@ struct rcar_gen3_cpg_pll_config {
 
 struct clk *rcar_gen3_cpg_clk_register(struct device *dev,
 	const struct cpg_core_clk *core, const struct cpg_mssr_info *info,
-	struct clk **clks, void __iomem *base);
+	struct clk **clks, void __iomem *base,
+	struct raw_notifier_head *notifiers);
 int rcar_gen3_cpg_init(const struct rcar_gen3_cpg_pll_config *config,
 		       unsigned int clk_extalr, u32 mode);
 
diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c
index 15fc8679d3426f3e..485dce00a562d919 100644
--- a/drivers/clk/renesas/renesas-cpg-mssr.c
+++ b/drivers/clk/renesas/renesas-cpg-mssr.c
@@ -107,6 +107,7 @@ static const u16 srcr[] = {
  * @num_core_clks: Number of Core Clocks in clks[]
  * @num_mod_clks: Number of Module Clocks in clks[]
  * @last_dt_core_clk: ID of the last Core Clock exported to DT
+ * @notifiers: Notifier chain to save/restore clock state for system resume
  * @smstpcr_saved[].mask: Mask of SMSTPCR[] bits under our control
  * @smstpcr_saved[].val: Saved values of SMSTPCR[]
  */
@@ -123,6 +124,7 @@ struct cpg_mssr_priv {
 	unsigned int num_mod_clks;
 	unsigned int last_dt_core_clk;
 
+	struct raw_notifier_head notifiers;
 	struct {
 		u32 mask;
 		u32 val;
@@ -312,7 +314,8 @@ static void __init cpg_mssr_register_core_clk(const struct cpg_core_clk *core,
 	default:
 		if (info->cpg_clk_register)
 			clk = info->cpg_clk_register(dev, core, info,
-						     priv->clks, priv->base);
+						     priv->clks, priv->base,
+						     &priv->notifiers);
 		else
 			dev_err(dev, "%s has unsupported core clock type %u\n",
 				core->name, core->type);
@@ -726,6 +729,9 @@ static int cpg_mssr_suspend_noirq(struct device *dev)
 				readl(priv->base + SMSTPCR(reg));
 	}
 
+	/* Save core clocks */
+	raw_notifier_call_chain(&priv->notifiers, PM_EVENT_SUSPEND, NULL);
+
 	return 0;
 }
 
@@ -739,6 +745,9 @@ static int cpg_mssr_resume_noirq(struct device *dev)
 	if (!psci_ops.cpu_suspend)
 		return 0;
 
+	/* Restore core clocks */
+	raw_notifier_call_chain(&priv->notifiers, PM_EVENT_RESUME, NULL);
+
 	/* Restore module clocks */
 	for (reg = 0; reg < ARRAY_SIZE(priv->smstpcr_saved); reg++) {
 		mask = priv->smstpcr_saved[reg].mask;
@@ -822,6 +831,7 @@ static int __init cpg_mssr_probe(struct platform_device *pdev)
 	priv->num_core_clks = info->num_total_core_clks;
 	priv->num_mod_clks = info->num_hw_mod_clks;
 	priv->last_dt_core_clk = info->last_dt_core_clk;
+	RAW_INIT_NOTIFIER_HEAD(&priv->notifiers);
 
 	for (i = 0; i < nclks; i++)
 		clks[i] = ERR_PTR(-ENOENT);
diff --git a/drivers/clk/renesas/renesas-cpg-mssr.h b/drivers/clk/renesas/renesas-cpg-mssr.h
index 66528ce3eb373fca..0745b09303082ef9 100644
--- a/drivers/clk/renesas/renesas-cpg-mssr.h
+++ b/drivers/clk/renesas/renesas-cpg-mssr.h
@@ -127,7 +127,8 @@ struct cpg_mssr_info {
 	struct clk *(*cpg_clk_register)(struct device *dev,
 					const struct cpg_core_clk *core,
 					const struct cpg_mssr_info *info,
-					struct clk **clks, void __iomem *base);
+					struct clk **clks, void __iomem *base,
+					struct raw_notifier_head *notifiers);
 };
 
 extern const struct cpg_mssr_info r8a7743_cpg_mssr_info;
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v3 3/5] clk: renesas: div6: Restore clock state during resume
  2017-10-16  8:36 [PATCH v3 0/5] clk: renesas: rcar-gen3: Restore clocks during resume Geert Uytterhoeven
  2017-10-16  8:36 ` [PATCH v3 1/5] clk: renesas: cpg-mssr: Restore module " Geert Uytterhoeven
  2017-10-16  8:36 ` [PATCH v3 2/5] clk: renesas: cpg-mssr: Add support to restore core " Geert Uytterhoeven
@ 2017-10-16  8:36 ` Geert Uytterhoeven
  2017-10-16  8:36 ` [PATCH v3 4/5] clk: renesas: rcar-gen3: Restore SDHI clocks " Geert Uytterhoeven
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Geert Uytterhoeven @ 2017-10-16  8:36 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd
  Cc: Niklas Söderlund, Laurent Pinchart, linux-renesas-soc,
	linux-clk, linux-pm, Geert Uytterhoeven

On R-Car Gen3 systems, PSCI system suspend powers down the SoC, losing
clock configuration.  Register an (optional) notifier to restore the
DIV6 clock state during system resume.

As DIV6 clocks can be picky w.r.t. modifying multiple register fields at
once, restore is not implemented by blindly restoring the register
value, but by using the existing cpg_div6_clock_{en,dis}able() helpers.

Note that this does not yet support DIV6 clocks with multiple parents,
which do not exist on R-Car Gen3 SoCs.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Tested-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
---
v3:
  - Drop RFC state,
  - Add Tested-by,

v2:
  - New.
---
 drivers/clk/renesas/clk-div6.c         | 38 ++++++++++++++++++++++++++++++++--
 drivers/clk/renesas/clk-div6.h         |  3 ++-
 drivers/clk/renesas/renesas-cpg-mssr.c |  3 ++-
 3 files changed, 40 insertions(+), 4 deletions(-)

diff --git a/drivers/clk/renesas/clk-div6.c b/drivers/clk/renesas/clk-div6.c
index 3e0040c0ac87a14b..151336d2ba59e689 100644
--- a/drivers/clk/renesas/clk-div6.c
+++ b/drivers/clk/renesas/clk-div6.c
@@ -14,8 +14,10 @@
 #include <linux/init.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
+#include <linux/notifier.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/pm.h>
 #include <linux/slab.h>
 
 #include "clk-div6.h"
@@ -32,6 +34,7 @@
  * @src_shift: Shift to access the register bits to select the parent clock
  * @src_width: Number of register bits to select the parent clock (may be 0)
  * @parents: Array to map from valid parent clocks indices to hardware indices
+ * @nb: Notifier block to save/restore clock state for system resume
  */
 struct div6_clock {
 	struct clk_hw hw;
@@ -40,6 +43,7 @@ struct div6_clock {
 	u32 src_shift;
 	u32 src_width;
 	u8 *parents;
+	struct notifier_block nb;
 };
 
 #define to_div6_clock(_hw) container_of(_hw, struct div6_clock, hw)
@@ -176,6 +180,29 @@ static const struct clk_ops cpg_div6_clock_ops = {
 	.set_rate = cpg_div6_clock_set_rate,
 };
 
+static int cpg_div6_clock_notifier_call(struct notifier_block *nb,
+					unsigned long action, void *data)
+{
+	struct div6_clock *clock = container_of(nb, struct div6_clock, nb);
+
+	switch (action) {
+	case PM_EVENT_RESUME:
+		/*
+		 * TODO: This does not yet support DIV6 clocks with multiple
+		 * parents, as the parent selection bits are not restored.
+		 * Fortunately so far such DIV6 clocks are found only on
+		 * R/SH-Mobile SoCs, while the resume functionality is only
+		 * needed on R-Car Gen3.
+		 */
+		if (__clk_get_enable_count(clock->hw.clk))
+			cpg_div6_clock_enable(&clock->hw);
+		else
+			cpg_div6_clock_disable(&clock->hw);
+		return NOTIFY_OK;
+	}
+
+	return NOTIFY_DONE;
+}
 
 /**
  * cpg_div6_register - Register a DIV6 clock
@@ -183,11 +210,13 @@ static const struct clk_ops cpg_div6_clock_ops = {
  * @num_parents: Number of parent clocks of the DIV6 clock (1, 4, or 8)
  * @parent_names: Array containing the names of the parent clocks
  * @reg: Mapped register used to control the DIV6 clock
+ * @notifiers: Optional notifier chain to save/restore state for system resume
  */
 struct clk * __init cpg_div6_register(const char *name,
 				      unsigned int num_parents,
 				      const char **parent_names,
-				      void __iomem *reg)
+				      void __iomem *reg,
+				      struct raw_notifier_head *notifiers)
 {
 	unsigned int valid_parents;
 	struct clk_init_data init;
@@ -258,6 +287,11 @@ struct clk * __init cpg_div6_register(const char *name,
 	if (IS_ERR(clk))
 		goto free_parents;
 
+	if (notifiers) {
+		clock->nb.notifier_call = cpg_div6_clock_notifier_call;
+		raw_notifier_chain_register(notifiers, &clock->nb);
+	}
+
 	return clk;
 
 free_parents:
@@ -301,7 +335,7 @@ static void __init cpg_div6_clock_init(struct device_node *np)
 	for (i = 0; i < num_parents; i++)
 		parent_names[i] = of_clk_get_parent_name(np, i);
 
-	clk = cpg_div6_register(clk_name, num_parents, parent_names, reg);
+	clk = cpg_div6_register(clk_name, num_parents, parent_names, reg, NULL);
 	if (IS_ERR(clk)) {
 		pr_err("%s: failed to register %s DIV6 clock (%ld)\n",
 		       __func__, np->name, PTR_ERR(clk));
diff --git a/drivers/clk/renesas/clk-div6.h b/drivers/clk/renesas/clk-div6.h
index 567b31d2bfa5269c..da4807299dda735b 100644
--- a/drivers/clk/renesas/clk-div6.h
+++ b/drivers/clk/renesas/clk-div6.h
@@ -2,6 +2,7 @@
 #define __RENESAS_CLK_DIV6_H__
 
 struct clk *cpg_div6_register(const char *name, unsigned int num_parents,
-			      const char **parent_names, void __iomem *reg);
+			      const char **parent_names, void __iomem *reg,
+			      struct raw_notifier_head *notifiers);
 
 #endif
diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c
index 485dce00a562d919..e3d03ffea4bc2fd6 100644
--- a/drivers/clk/renesas/renesas-cpg-mssr.c
+++ b/drivers/clk/renesas/renesas-cpg-mssr.c
@@ -303,7 +303,8 @@ static void __init cpg_mssr_register_core_clk(const struct cpg_core_clk *core,
 
 		if (core->type == CLK_TYPE_DIV6P1) {
 			clk = cpg_div6_register(core->name, 1, &parent_name,
-						priv->base + core->offset);
+						priv->base + core->offset,
+						&priv->notifiers);
 		} else {
 			clk = clk_register_fixed_factor(NULL, core->name,
 							parent_name, 0,
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v3 4/5] clk: renesas: rcar-gen3: Restore SDHI clocks during resume
  2017-10-16  8:36 [PATCH v3 0/5] clk: renesas: rcar-gen3: Restore clocks during resume Geert Uytterhoeven
                   ` (2 preceding siblings ...)
  2017-10-16  8:36 ` [PATCH v3 3/5] clk: renesas: div6: Restore clock state " Geert Uytterhoeven
@ 2017-10-16  8:36 ` Geert Uytterhoeven
  2017-10-16  8:36 ` [PATCH v3 5/5] clk: renesas: rcar-gen3: Restore R clock " Geert Uytterhoeven
  2017-10-20  9:32 ` [PATCH v3 0/5] clk: renesas: rcar-gen3: Restore clocks " Geert Uytterhoeven
  5 siblings, 0 replies; 7+ messages in thread
From: Geert Uytterhoeven @ 2017-10-16  8:36 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd
  Cc: Niklas Söderlund, Laurent Pinchart, linux-renesas-soc,
	linux-clk, linux-pm, Geert Uytterhoeven

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>
---
v3:
  - Drop RFC state,
  - Add Tested-by,
  - Rebased,

v2:
  - New.
---
 drivers/clk/renesas/rcar-gen3-cpg.c | 63 +++++++++++++++++++++++++++++--------
 1 file changed, 50 insertions(+), 13 deletions(-)

diff --git a/drivers/clk/renesas/rcar-gen3-cpg.c b/drivers/clk/renesas/rcar-gen3-cpg.c
index 139985257003bfad..267b5629e3bdb173 100644
--- a/drivers/clk/renesas/rcar-gen3-cpg.c
+++ b/drivers/clk/renesas/rcar-gen3-cpg.c
@@ -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>
 
@@ -29,6 +30,36 @@
 #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) {
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v3 5/5] clk: renesas: rcar-gen3: Restore R clock during resume
  2017-10-16  8:36 [PATCH v3 0/5] clk: renesas: rcar-gen3: Restore clocks during resume Geert Uytterhoeven
                   ` (3 preceding siblings ...)
  2017-10-16  8:36 ` [PATCH v3 4/5] clk: renesas: rcar-gen3: Restore SDHI clocks " Geert Uytterhoeven
@ 2017-10-16  8:36 ` Geert Uytterhoeven
  2017-10-20  9:32 ` [PATCH v3 0/5] clk: renesas: rcar-gen3: Restore clocks " Geert Uytterhoeven
  5 siblings, 0 replies; 7+ messages in thread
From: Geert Uytterhoeven @ 2017-10-16  8:36 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd
  Cc: Niklas Söderlund, Laurent Pinchart, linux-renesas-soc,
	linux-clk, linux-pm, Geert Uytterhoeven

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

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Tested-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
---
v3:
  - Drop RFC state,
  - Add Tested-by,

v2:
  - New.
---
 drivers/clk/renesas/rcar-gen3-cpg.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/renesas/rcar-gen3-cpg.c b/drivers/clk/renesas/rcar-gen3-cpg.c
index 267b5629e3bdb173..0904886f55015a3b 100644
--- a/drivers/clk/renesas/rcar-gen3-cpg.c
+++ b/drivers/clk/renesas/rcar-gen3-cpg.c
@@ -373,18 +373,27 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev,
 
 	case CLK_TYPE_GEN3_R:
 		if (cpg_quirks & RCKCR_CKSEL) {
+			struct cpg_simple_notifier *csn;
+
+			csn = kzalloc(sizeof(*csn), GFP_KERNEL);
+			if (!csn)
+				return ERR_PTR(-ENOMEM);
+
+			csn->reg = base + CPG_RCKCR;
+
 			/*
 			 * RINT is default.
 			 * Only if EXTALR is populated, we switch to it.
 			 */
-			value = readl(base + CPG_RCKCR) & 0x3f;
+			value = readl(csn->reg) & 0x3f;
 
 			if (clk_get_rate(clks[cpg_clk_extalr])) {
 				parent = clks[cpg_clk_extalr];
 				value |= BIT(15);
 			}
 
-			writel(value, base + CPG_RCKCR);
+			writel(value, csn->reg);
+			cpg_simple_notifier_register(notifiers, csn);
 			break;
 		}
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH v3 0/5] clk: renesas: rcar-gen3: Restore clocks during resume
  2017-10-16  8:36 [PATCH v3 0/5] clk: renesas: rcar-gen3: Restore clocks during resume Geert Uytterhoeven
                   ` (4 preceding siblings ...)
  2017-10-16  8:36 ` [PATCH v3 5/5] clk: renesas: rcar-gen3: Restore R clock " Geert Uytterhoeven
@ 2017-10-20  9:32 ` Geert Uytterhoeven
  5 siblings, 0 replies; 7+ messages in thread
From: Geert Uytterhoeven @ 2017-10-20  9:32 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Michael Turquette, Stephen Boyd, Niklas Söderlund,
	Laurent Pinchart, Linux-Renesas, linux-clk, Linux PM list

On Mon, Oct 16, 2017 at 10:36 AM, Geert Uytterhoeven
<geert+renesas@glider.be> wrote:
> During PSCI system suspend, R-Car Gen3 SoCs are powered down, and their
> clock register state is lost.  Note that as the boot loader skips most
> initialization after resume, clock register state differs from the state
> encountered during normal system boot, too.
>
> Hence after s2ram, some operations may fail because module clocks are
> disabled, while drivers expect them to be still enabled.  E.g. in the
> absence of commit fbf3d034f2ff6264 ("ravb: add workaround for clock when
> resuming with WoL enabled"), EtherAVB fails when Wake-on-LAN has been
> enabled using "ethtool -s eth0 wol g":
>
>     ravb e6800000.ethernet eth0: failed to switch device to config mode
>     ravb e6800000.ethernet eth0: device will be stopped after h/w processes are done.
>     ravb e6800000.ethernet eth0: failed to switch device to config
>     PM: Device e6800000.ethernet failed to resume: error -110
>
> In addition, some clocks that were disabled by clk_disable_unused() may
> have been re-enabled, wasting power.
>
> This patch series fixes these issues by restoring clock registers during
> system resume.  Note that while this fixes EtherAVB operation after
> resume from s2ram, EtherAVB cannot be used as an actual wake-up source
> from s2ram, only from s2idle, due to PSCI limitations.
>
> To avoid overhead on platforms not needing it, the suspend/resume code
> has a build time dependency on sleep and PSCI support, and a runtime
> dependency on PSCI.
>
> Changes compared to v2:
>   - Drop RFC state,
>   - Add Tested-by,
>   - Add build and runtime dependencies on PM_SLEEP and PSCI.
>
> Changes compared to v1 (more details in the individual patches):
>   - Save module clock registers in suspend_noirq instead of constantly
>     updating shadow registers,
>   - Restore all module clocks under our control, not just the ones we ever
>     changed,
>   - Also restore DIV6, SDHI, and R clocks, thus covering all supported
>     programmable core clocks on R-Car Gen3.
>
> This series is against the clk-renesas branch of my renesas-drivers git
> repository at
> git://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-drivers.git.
> For your convenience, this series is also available in the
> topic/cpg-mssr-suspend-resume-v3 branch of the same repository.
>
> This has been tested on Salvator-X with R-Car H3 ES1.0 and M3-W ES1.0,
> and on Salvator-XS with R-Car H3 ES2.0 (the latter also needs "[PATCH
> 0/4] ravb: Add PHY reset support",
> https://www.spinics.net/lists/netdev/msg457308.html), and
> regression-tested on various other Renesas arm32 boards not using PSCI.
>
> I plan to queue this up in clk-renesas-for-v4.15.

Done.

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2017-10-20  9:32 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-10-16  8:36 [PATCH v3 0/5] clk: renesas: rcar-gen3: Restore clocks during resume Geert Uytterhoeven
2017-10-16  8:36 ` [PATCH v3 1/5] clk: renesas: cpg-mssr: Restore module " Geert Uytterhoeven
2017-10-16  8:36 ` [PATCH v3 2/5] clk: renesas: cpg-mssr: Add support to restore core " Geert Uytterhoeven
2017-10-16  8:36 ` [PATCH v3 3/5] clk: renesas: div6: Restore clock state " Geert Uytterhoeven
2017-10-16  8:36 ` [PATCH v3 4/5] clk: renesas: rcar-gen3: Restore SDHI clocks " Geert Uytterhoeven
2017-10-16  8:36 ` [PATCH v3 5/5] clk: renesas: rcar-gen3: Restore R clock " Geert Uytterhoeven
2017-10-20  9:32 ` [PATCH v3 0/5] clk: renesas: rcar-gen3: Restore clocks " Geert Uytterhoeven

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.