Skip to content

Commit 5608691

Browse files
committed
clk: renesas: cpg-mssr: Restore module clocks during resume
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 <[email protected]> Tested-by: Niklas Söderlund <[email protected]>
1 parent d9341f2 commit 5608691

File tree

1 file changed

+84
-0
lines changed

1 file changed

+84
-0
lines changed

drivers/clk/renesas/renesas-cpg-mssr.c

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <linux/platform_device.h>
2727
#include <linux/pm_clock.h>
2828
#include <linux/pm_domain.h>
29+
#include <linux/psci.h>
2930
#include <linux/reset-controller.h>
3031
#include <linux/slab.h>
3132

@@ -106,6 +107,8 @@ static const u16 srcr[] = {
106107
* @num_core_clks: Number of Core Clocks in clks[]
107108
* @num_mod_clks: Number of Module Clocks in clks[]
108109
* @last_dt_core_clk: ID of the last Core Clock exported to DT
110+
* @smstpcr_saved[].mask: Mask of SMSTPCR[] bits under our control
111+
* @smstpcr_saved[].val: Saved values of SMSTPCR[]
109112
*/
110113
struct cpg_mssr_priv {
111114
#ifdef CONFIG_RESET_CONTROLLER
@@ -119,6 +122,11 @@ struct cpg_mssr_priv {
119122
unsigned int num_core_clks;
120123
unsigned int num_mod_clks;
121124
unsigned int last_dt_core_clk;
125+
126+
struct {
127+
u32 mask;
128+
u32 val;
129+
} smstpcr_saved[ARRAY_SIZE(smstpcr)];
122130
};
123131

124132

@@ -382,6 +390,7 @@ static void __init cpg_mssr_register_mod_clk(const struct mssr_mod_clk *mod,
382390

383391
dev_dbg(dev, "Module clock %pC at %pCr Hz\n", clk, clk);
384392
priv->clks[id] = clk;
393+
priv->smstpcr_saved[clock->index / 32].mask |= BIT(clock->index % 32);
385394
return;
386395

387396
fail:
@@ -700,6 +709,79 @@ static void cpg_mssr_del_clk_provider(void *data)
700709
of_clk_del_provider(data);
701710
}
702711

712+
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_ARM_PSCI_FW)
713+
static int cpg_mssr_suspend_noirq(struct device *dev)
714+
{
715+
struct cpg_mssr_priv *priv = dev_get_drvdata(dev);
716+
unsigned int reg;
717+
718+
/* This is the best we can do to check for the presence of PSCI */
719+
if (!psci_ops.cpu_suspend)
720+
return 0;
721+
722+
/* Save module registers with bits under our control */
723+
for (reg = 0; reg < ARRAY_SIZE(priv->smstpcr_saved); reg++) {
724+
if (priv->smstpcr_saved[reg].mask)
725+
priv->smstpcr_saved[reg].val =
726+
readl(priv->base + SMSTPCR(reg));
727+
}
728+
729+
return 0;
730+
}
731+
732+
static int cpg_mssr_resume_noirq(struct device *dev)
733+
{
734+
struct cpg_mssr_priv *priv = dev_get_drvdata(dev);
735+
unsigned int reg, i;
736+
u32 mask, oldval, newval;
737+
738+
/* This is the best we can do to check for the presence of PSCI */
739+
if (!psci_ops.cpu_suspend)
740+
return 0;
741+
742+
/* Restore module clocks */
743+
for (reg = 0; reg < ARRAY_SIZE(priv->smstpcr_saved); reg++) {
744+
mask = priv->smstpcr_saved[reg].mask;
745+
if (!mask)
746+
continue;
747+
748+
oldval = readl(priv->base + SMSTPCR(reg));
749+
newval = oldval & ~mask;
750+
newval |= priv->smstpcr_saved[reg].val & mask;
751+
if (newval == oldval)
752+
continue;
753+
754+
writel(newval, priv->base + SMSTPCR(reg));
755+
756+
/* Wait until enabled clocks are really enabled */
757+
mask &= ~priv->smstpcr_saved[reg].val;
758+
if (!mask)
759+
continue;
760+
761+
for (i = 1000; i > 0; --i) {
762+
oldval = readl(priv->base + MSTPSR(reg));
763+
if (!(oldval & mask))
764+
break;
765+
cpu_relax();
766+
}
767+
768+
if (!i)
769+
dev_warn(dev, "Failed to enable SMSTP %p[0x%x]\n",
770+
priv->base + SMSTPCR(reg), oldval & mask);
771+
}
772+
773+
return 0;
774+
}
775+
776+
static const struct dev_pm_ops cpg_mssr_pm = {
777+
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(cpg_mssr_suspend_noirq,
778+
cpg_mssr_resume_noirq)
779+
};
780+
#define DEV_PM_OPS &cpg_mssr_pm
781+
#else
782+
#define DEV_PM_OPS NULL
783+
#endif /* CONFIG_PM_SLEEP && CONFIG_ARM_PSCI_FW */
784+
703785
static int __init cpg_mssr_probe(struct platform_device *pdev)
704786
{
705787
struct device *dev = &pdev->dev;
@@ -735,6 +817,7 @@ static int __init cpg_mssr_probe(struct platform_device *pdev)
735817
if (!clks)
736818
return -ENOMEM;
737819

820+
dev_set_drvdata(dev, priv);
738821
priv->clks = clks;
739822
priv->num_core_clks = info->num_total_core_clks;
740823
priv->num_mod_clks = info->num_hw_mod_clks;
@@ -775,6 +858,7 @@ static struct platform_driver cpg_mssr_driver = {
775858
.driver = {
776859
.name = "renesas-cpg-mssr",
777860
.of_match_table = cpg_mssr_match,
861+
.pm = DEV_PM_OPS,
778862
},
779863
};
780864

0 commit comments

Comments
 (0)