aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clk/renesas/renesas-cpg-mssr.c
diff options
context:
space:
mode:
authorGeert Uytterhoeven <geert+renesas@glider.be>2017-06-07 07:20:06 -0400
committerGeert Uytterhoeven <geert+renesas@glider.be>2017-10-20 05:15:29 -0400
commit560869100b99a3daea329efce738a3b7ae357be8 (patch)
tree68cf1b8145d8646a1c5127d835e79e90ce9c7b77 /drivers/clk/renesas/renesas-cpg-mssr.c
parentd9341f2b00c0f2a50e1fada55f6a2a2c51818ae6 (diff)
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 <geert+renesas@glider.be> Tested-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Diffstat (limited to 'drivers/clk/renesas/renesas-cpg-mssr.c')
-rw-r--r--drivers/clk/renesas/renesas-cpg-mssr.c84
1 files changed, 84 insertions, 0 deletions
diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c
index 1779b0cc7a2a..15fc8679d342 100644
--- a/drivers/clk/renesas/renesas-cpg-mssr.c
+++ b/drivers/clk/renesas/renesas-cpg-mssr.c
@@ -26,6 +26,7 @@
26#include <linux/platform_device.h> 26#include <linux/platform_device.h>
27#include <linux/pm_clock.h> 27#include <linux/pm_clock.h>
28#include <linux/pm_domain.h> 28#include <linux/pm_domain.h>
29#include <linux/psci.h>
29#include <linux/reset-controller.h> 30#include <linux/reset-controller.h>
30#include <linux/slab.h> 31#include <linux/slab.h>
31 32
@@ -106,6 +107,8 @@ static const u16 srcr[] = {
106 * @num_core_clks: Number of Core Clocks in clks[] 107 * @num_core_clks: Number of Core Clocks in clks[]
107 * @num_mod_clks: Number of Module Clocks in clks[] 108 * @num_mod_clks: Number of Module Clocks in clks[]
108 * @last_dt_core_clk: ID of the last Core Clock exported to DT 109 * @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[]
109 */ 112 */
110struct cpg_mssr_priv { 113struct cpg_mssr_priv {
111#ifdef CONFIG_RESET_CONTROLLER 114#ifdef CONFIG_RESET_CONTROLLER
@@ -119,6 +122,11 @@ struct cpg_mssr_priv {
119 unsigned int num_core_clks; 122 unsigned int num_core_clks;
120 unsigned int num_mod_clks; 123 unsigned int num_mod_clks;
121 unsigned int last_dt_core_clk; 124 unsigned int last_dt_core_clk;
125
126 struct {
127 u32 mask;
128 u32 val;
129 } smstpcr_saved[ARRAY_SIZE(smstpcr)];
122}; 130};
123 131
124 132
@@ -382,6 +390,7 @@ static void __init cpg_mssr_register_mod_clk(const struct mssr_mod_clk *mod,
382 390
383 dev_dbg(dev, "Module clock %pC at %pCr Hz\n", clk, clk); 391 dev_dbg(dev, "Module clock %pC at %pCr Hz\n", clk, clk);
384 priv->clks[id] = clk; 392 priv->clks[id] = clk;
393 priv->smstpcr_saved[clock->index / 32].mask |= BIT(clock->index % 32);
385 return; 394 return;
386 395
387fail: 396fail:
@@ -700,6 +709,79 @@ static void cpg_mssr_del_clk_provider(void *data)
700 of_clk_del_provider(data); 709 of_clk_del_provider(data);
701} 710}
702 711
712#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_ARM_PSCI_FW)
713static 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
732static 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
776static 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
703static int __init cpg_mssr_probe(struct platform_device *pdev) 785static int __init cpg_mssr_probe(struct platform_device *pdev)
704{ 786{
705 struct device *dev = &pdev->dev; 787 struct device *dev = &pdev->dev;
@@ -735,6 +817,7 @@ static int __init cpg_mssr_probe(struct platform_device *pdev)
735 if (!clks) 817 if (!clks)
736 return -ENOMEM; 818 return -ENOMEM;
737 819
820 dev_set_drvdata(dev, priv);
738 priv->clks = clks; 821 priv->clks = clks;
739 priv->num_core_clks = info->num_total_core_clks; 822 priv->num_core_clks = info->num_total_core_clks;
740 priv->num_mod_clks = info->num_hw_mod_clks; 823 priv->num_mod_clks = info->num_hw_mod_clks;
@@ -775,6 +858,7 @@ static struct platform_driver cpg_mssr_driver = {
775 .driver = { 858 .driver = {
776 .name = "renesas-cpg-mssr", 859 .name = "renesas-cpg-mssr",
777 .of_match_table = cpg_mssr_match, 860 .of_match_table = cpg_mssr_match,
861 .pm = DEV_PM_OPS,
778 }, 862 },
779}; 863};
780 864