devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v7 00/20] SC7 entry and exit support for Tegra210
@ 2019-07-31  0:20 Sowjanya Komatineni
  2019-07-31  0:20 ` [PATCH v7 01/20] pinctrl: tegra: Add suspend and resume support Sowjanya Komatineni
                   ` (19 more replies)
  0 siblings, 20 replies; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-07-31  0:20 UTC (permalink / raw)
  To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
	linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, skomatineni, linux-tegra, linux-kernel,
	mperttunen, spatra, robh+dt, digetx, devicetree

This patch series includes Tegra210 deepsleep (SC7) support with RTC alarm
wake event.

This series also includes save and restore of PLLs, clocks, OSC contexts
for deepsleep exit to normal operation.

This patch series doesn't support 100% suspend/resume to allow fully
functional state upon resume and we are working on some more drivers suspend
and resume implementations.

[V7]: Changes between V6 & V7 are
	- V6 feedback fixes
	- Removed patch-0001 from V6 which keeps COP IRQ enabled. Looking
	  more into ATF FW, it loads SC7 entry FW into IRAM and sets the
	  COP reset vector to SC7 FW load address and resets COP. So, COP
	  IRQ can be cleared during suspend.

	Note:
	  Below patch is also needed for SC7 support as GPIO restore need
	  to happen prior to pinctrl.
	  https://patchwork.kernel.org/patch/11012077/

[V6]: Changes between V5 & V6 are
	- V5 feedback fixes
	- DFLL suspend and resume moved to DFLL clock driver
	- Add suspend and resume support for CPUFreq driver to explicitly
	  switch source to safe source of PLLP and disable DFLL clock.
	- Fix to super clock driver to enable PLLP branch to CPU before
	  source switch to PLLP.
	- Added save and restore support for super clock driver.

[V5]: Changes between V4 & V5 are
	- V4 feedback fixes

[V4]: Changes between V3 & V4 are
	- V3 feedback fixes
	- Removed park bits clear for EMMC pads in pinctrl-tegra driver
	  function tegra_pinctrl_clear_parked_bits as based on V3 feedback
	  parked_bit is updated to parked_bitmask to use with DRV_PINGROUP
	  as well and thierry posted patch series for this.
	- Implemented all peripheral clocks save and restore through their
	  corresponding clk_ops save_context and restore_context and removed
	  all direct registers store and restore in clk-tegra210 driver.
	- Created separate patch for fence_delay update during PLLU init based
	  on V3 feedback.
	- Added more comments in tegra210_clk_resume regarding dfll restore
	  sequence and its dependency on peripheral clocks restore.

[V3]: Changes between V2 & V3 are
	- V2 feedback fixes
	- GPIO restore should happen prior to Pinctrl restore to prevent
	  glitch on GPIO lines. So using resume_noirq for gpio tegra to allow
	  gpio resume prior to pinctrl resume.
	- Implemented save_context and restore_context callbacks for clock
	  plls, pll outs and dividers in corresponding drivers.
	  Note: Peripheral clocks and clock enable and reset need to be in
	  Tegra210 clock suspend/resume as they need to be in proper sequence
	  w.r.t DFLL resume for restoring CPU clock.
	- Removed gpio-tegra changes for hierarchical support to have PMC as
	  parent to GPIOs for GPIO wake event support. Thierry is working on
	  gpiolib for some cleanup before adding hierarchical support. So
	  holding on to GPIO wake support for now.

[V2] : V1 feedback fixes
	Patch 0002: This version still using syscore. Thierry suggest not to
	use syscore and waiting on suggestion from Linux Walleij for any better
	way of storing current state of pins before suspend entry and restoring
	them on resume at very early stage. So left this the same way as V1 and
	will address once I get more feedback on this.
	Also need to findout and implement proper way of forcing resume order
	between pinctrl and gpio driver.

[V1]:	Tegra210 SC7 entry and exit thru RTC wake and Power button GPIO wake
	using hierarchical IRQ with PMC as parent to GPIO.



Sowjanya Komatineni (20):
  pinctrl: tegra: Add suspend and resume support
  pinctrl: tegra210: Add Tegra210 pinctrl pm ops
  clk: tegra: divider: Save and restore divider rate
  clk: tegra: pllout: Save and restore pllout context
  clk: tegra: pll: Save and restore pll context
  clk: tegra: Support for OSC context save and restore
  clk: tegra: clk-periph: Add save and restore support
  clk: tegra: clk-super: Fix to enable PLLP branches to CPU
  clk: tegra: clk-super: Add save and restore support
  clk: tegra: clk-dfll: Add suspend and resume support
  cpufreq: tegra124: Add suspend and resume support
  clk: tegra210: Use fence_udelay during PLLU init
  clk: tegra210: Add suspend and resume support
  soc/tegra: pmc: Allow to support more tegras wake
  soc/tegra: pmc: Add pmc wake support for tegra210
  arm64: tegra: Enable wake from deep sleep on RTC alarm
  soc/tegra: pmc: Configure core power request polarity
  soc/tegra: pmc: Configure deep sleep control settings
  arm64: dts: tegra210-p2180: Jetson TX1 SC7 timings
  arm64: dts: tegra210-p3450: Jetson Nano SC7 timings

 arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi     |   7 ++
 arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts |   7 ++
 arch/arm64/boot/dts/nvidia/tegra210.dtsi           |   5 +-
 drivers/clk/tegra/clk-dfll.c                       |  56 +++++++++
 drivers/clk/tegra/clk-dfll.h                       |   2 +
 drivers/clk/tegra/clk-divider.c                    |  11 ++
 drivers/clk/tegra/clk-periph-fixed.c               |  33 ++++++
 drivers/clk/tegra/clk-periph-gate.c                |  34 ++++++
 drivers/clk/tegra/clk-periph.c                     |  37 ++++++
 drivers/clk/tegra/clk-pll-out.c                    |  26 +++++
 drivers/clk/tegra/clk-pll.c                        | 112 +++++++++++++-----
 drivers/clk/tegra/clk-sdmmc-mux.c                  |  28 +++++
 drivers/clk/tegra/clk-super.c                      |  53 +++++++++
 drivers/clk/tegra/clk-tegra-fixed.c                |  15 +++
 drivers/clk/tegra/clk-tegra-super-gen4.c           |   2 +-
 drivers/clk/tegra/clk-tegra124-dfll-fcpu.c         |   1 +
 drivers/clk/tegra/clk-tegra210.c                   |  86 ++++++++++++--
 drivers/clk/tegra/clk.c                            |  14 +++
 drivers/clk/tegra/clk.h                            |  26 +++++
 drivers/cpufreq/tegra124-cpufreq.c                 |  60 ++++++++++
 drivers/pinctrl/tegra/pinctrl-tegra.c              |  59 ++++++++++
 drivers/pinctrl/tegra/pinctrl-tegra.h              |   3 +
 drivers/pinctrl/tegra/pinctrl-tegra210.c           |   1 +
 drivers/soc/tegra/pmc.c                            | 129 ++++++++++++++++++++-
 24 files changed, 759 insertions(+), 48 deletions(-)

-- 
2.7.4

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

* [PATCH v7 01/20] pinctrl: tegra: Add suspend and resume support
  2019-07-31  0:20 [PATCH v7 00/20] SC7 entry and exit support for Tegra210 Sowjanya Komatineni
@ 2019-07-31  0:20 ` Sowjanya Komatineni
  2019-07-31  0:20 ` [PATCH v7 02/20] pinctrl: tegra210: Add Tegra210 pinctrl pm ops Sowjanya Komatineni
                   ` (18 subsequent siblings)
  19 siblings, 0 replies; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-07-31  0:20 UTC (permalink / raw)
  To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
	linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, skomatineni, linux-tegra, linux-kernel,
	mperttunen, spatra, robh+dt, digetx, devicetree

This patch adds support for Tegra pinctrl driver suspend and resume.

During suspend, context of all pinctrl registers are stored and
on resume they are all restored to have all the pinmux and pad
configuration for normal operation.

Acked-by: Thierry Reding <treding@nvidia.com>
Reviewed-by: Dmitry Osipenko <digetx@gmail.com>
Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/pinctrl/tegra/pinctrl-tegra.c | 59 +++++++++++++++++++++++++++++++++++
 drivers/pinctrl/tegra/pinctrl-tegra.h |  3 ++
 2 files changed, 62 insertions(+)

diff --git a/drivers/pinctrl/tegra/pinctrl-tegra.c b/drivers/pinctrl/tegra/pinctrl-tegra.c
index 186ef98e7b2b..e3a237534281 100644
--- a/drivers/pinctrl/tegra/pinctrl-tegra.c
+++ b/drivers/pinctrl/tegra/pinctrl-tegra.c
@@ -631,6 +631,58 @@ static void tegra_pinctrl_clear_parked_bits(struct tegra_pmx *pmx)
 	}
 }
 
+static size_t tegra_pinctrl_get_bank_size(struct device *dev,
+					  unsigned int bank_id)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct resource *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, bank_id);
+
+	return resource_size(res) / 4;
+}
+
+static int tegra_pinctrl_suspend(struct device *dev)
+{
+	struct tegra_pmx *pmx = dev_get_drvdata(dev);
+	u32 *backup_regs = pmx->backup_regs;
+	u32 *regs;
+	size_t bank_size;
+	unsigned int i, k;
+
+	for (i = 0; i < pmx->nbanks; i++) {
+		bank_size = tegra_pinctrl_get_bank_size(dev, i);
+		regs = pmx->regs[i];
+		for (k = 0; k < bank_size; k++)
+			*backup_regs++ = readl_relaxed(regs++);
+	}
+
+	return pinctrl_force_sleep(pmx->pctl);
+}
+
+static int tegra_pinctrl_resume(struct device *dev)
+{
+	struct tegra_pmx *pmx = dev_get_drvdata(dev);
+	u32 *backup_regs = pmx->backup_regs;
+	u32 *regs;
+	size_t bank_size;
+	unsigned int i, k;
+
+	for (i = 0; i < pmx->nbanks; i++) {
+		bank_size = tegra_pinctrl_get_bank_size(dev, i);
+		regs = pmx->regs[i];
+		for (k = 0; k < bank_size; k++)
+			writel_relaxed(*backup_regs++, regs++);
+	}
+
+	return 0;
+}
+
+const struct dev_pm_ops tegra_pinctrl_pm = {
+	.suspend = &tegra_pinctrl_suspend,
+	.resume = &tegra_pinctrl_resume
+};
+
 static bool gpio_node_has_range(const char *compatible)
 {
 	struct device_node *np;
@@ -655,6 +707,7 @@ int tegra_pinctrl_probe(struct platform_device *pdev,
 	int i;
 	const char **group_pins;
 	int fn, gn, gfn;
+	unsigned long backup_regs_size = 0;
 
 	pmx = devm_kzalloc(&pdev->dev, sizeof(*pmx), GFP_KERNEL);
 	if (!pmx)
@@ -707,6 +760,7 @@ int tegra_pinctrl_probe(struct platform_device *pdev,
 		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
 		if (!res)
 			break;
+		backup_regs_size += resource_size(res);
 	}
 	pmx->nbanks = i;
 
@@ -715,6 +769,11 @@ int tegra_pinctrl_probe(struct platform_device *pdev,
 	if (!pmx->regs)
 		return -ENOMEM;
 
+	pmx->backup_regs = devm_kzalloc(&pdev->dev, backup_regs_size,
+					GFP_KERNEL);
+	if (!pmx->backup_regs)
+		return -ENOMEM;
+
 	for (i = 0; i < pmx->nbanks; i++) {
 		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
 		pmx->regs[i] = devm_ioremap_resource(&pdev->dev, res);
diff --git a/drivers/pinctrl/tegra/pinctrl-tegra.h b/drivers/pinctrl/tegra/pinctrl-tegra.h
index 105309774079..0fc82eea9cf1 100644
--- a/drivers/pinctrl/tegra/pinctrl-tegra.h
+++ b/drivers/pinctrl/tegra/pinctrl-tegra.h
@@ -17,6 +17,7 @@ struct tegra_pmx {
 
 	int nbanks;
 	void __iomem **regs;
+	u32 *backup_regs;
 };
 
 enum tegra_pinconf_param {
@@ -193,6 +194,8 @@ struct tegra_pinctrl_soc_data {
 	bool drvtype_in_mux;
 };
 
+extern const struct dev_pm_ops tegra_pinctrl_pm;
+
 int tegra_pinctrl_probe(struct platform_device *pdev,
 			const struct tegra_pinctrl_soc_data *soc_data);
 #endif
-- 
2.7.4

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

* [PATCH v7 02/20] pinctrl: tegra210: Add Tegra210 pinctrl pm ops
  2019-07-31  0:20 [PATCH v7 00/20] SC7 entry and exit support for Tegra210 Sowjanya Komatineni
  2019-07-31  0:20 ` [PATCH v7 01/20] pinctrl: tegra: Add suspend and resume support Sowjanya Komatineni
@ 2019-07-31  0:20 ` Sowjanya Komatineni
  2019-07-31  0:20 ` [PATCH v7 03/20] clk: tegra: divider: Save and restore divider rate Sowjanya Komatineni
                   ` (17 subsequent siblings)
  19 siblings, 0 replies; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-07-31  0:20 UTC (permalink / raw)
  To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
	linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, skomatineni, linux-tegra, linux-kernel,
	mperttunen, spatra, robh+dt, digetx, devicetree

This patch adds suspend and resume functionality to Tegra210 pinctrl.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/pinctrl/tegra/pinctrl-tegra210.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/pinctrl/tegra/pinctrl-tegra210.c b/drivers/pinctrl/tegra/pinctrl-tegra210.c
index 39ab6480a941..fc072a36deb3 100644
--- a/drivers/pinctrl/tegra/pinctrl-tegra210.c
+++ b/drivers/pinctrl/tegra/pinctrl-tegra210.c
@@ -1571,6 +1571,7 @@ static struct platform_driver tegra210_pinctrl_driver = {
 	.driver = {
 		.name = "tegra210-pinctrl",
 		.of_match_table = tegra210_pinctrl_of_match,
+		.pm = &tegra_pinctrl_pm,
 	},
 	.probe = tegra210_pinctrl_probe,
 };
-- 
2.7.4

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

* [PATCH v7 03/20] clk: tegra: divider: Save and restore divider rate
  2019-07-31  0:20 [PATCH v7 00/20] SC7 entry and exit support for Tegra210 Sowjanya Komatineni
  2019-07-31  0:20 ` [PATCH v7 01/20] pinctrl: tegra: Add suspend and resume support Sowjanya Komatineni
  2019-07-31  0:20 ` [PATCH v7 02/20] pinctrl: tegra210: Add Tegra210 pinctrl pm ops Sowjanya Komatineni
@ 2019-07-31  0:20 ` Sowjanya Komatineni
  2019-07-31 10:49   ` Dmitry Osipenko
  2019-07-31  0:20 ` [PATCH v7 04/20] clk: tegra: pllout: Save and restore pllout context Sowjanya Komatineni
                   ` (16 subsequent siblings)
  19 siblings, 1 reply; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-07-31  0:20 UTC (permalink / raw)
  To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
	linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, skomatineni, linux-tegra, linux-kernel,
	mperttunen, spatra, robh+dt, digetx, devicetree

This patch implements context restore for clock divider.

During system suspend, core power goes off and looses the settings
of the Tegra CAR controller registers.

So on resume, clock dividers are restored back for normal operation.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/clk/tegra/clk-divider.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/drivers/clk/tegra/clk-divider.c b/drivers/clk/tegra/clk-divider.c
index e76731fb7d69..ca0de5f11f84 100644
--- a/drivers/clk/tegra/clk-divider.c
+++ b/drivers/clk/tegra/clk-divider.c
@@ -109,10 +109,21 @@ static int clk_frac_div_set_rate(struct clk_hw *hw, unsigned long rate,
 	return 0;
 }
 
+static void clk_divider_restore_context(struct clk_hw *hw)
+{
+	struct clk_hw *parent = clk_hw_get_parent(hw);
+	unsigned long parent_rate = clk_hw_get_rate(parent);
+	unsigned long rate = clk_hw_get_rate(hw);
+
+	if (clk_frac_div_set_rate(hw, rate, parent_rate) < 0)
+		WARN_ON(1);
+}
+
 const struct clk_ops tegra_clk_frac_div_ops = {
 	.recalc_rate = clk_frac_div_recalc_rate,
 	.set_rate = clk_frac_div_set_rate,
 	.round_rate = clk_frac_div_round_rate,
+	.restore_context = clk_divider_restore_context,
 };
 
 struct clk *tegra_clk_register_divider(const char *name,
-- 
2.7.4

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

* [PATCH v7 04/20] clk: tegra: pllout: Save and restore pllout context
  2019-07-31  0:20 [PATCH v7 00/20] SC7 entry and exit support for Tegra210 Sowjanya Komatineni
                   ` (2 preceding siblings ...)
  2019-07-31  0:20 ` [PATCH v7 03/20] clk: tegra: divider: Save and restore divider rate Sowjanya Komatineni
@ 2019-07-31  0:20 ` Sowjanya Komatineni
  2019-07-31  0:20 ` [PATCH v7 05/20] clk: tegra: pll: Save and restore pll context Sowjanya Komatineni
                   ` (15 subsequent siblings)
  19 siblings, 0 replies; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-07-31  0:20 UTC (permalink / raw)
  To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
	linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, skomatineni, linux-tegra, linux-kernel,
	mperttunen, spatra, robh+dt, digetx, devicetree

This patch implements save and restore of pllout context.

During system suspend, core power goes off and looses the settings
of the Tegra CAR controller registers.

So during suspend entry the state of pllout is saved and on resume
it is restored back to have pllout in same state as before suspend.

pllout rate is saved and restore in clock divider so it will be at
same rate as before suspend when pllout state is restored.

Acked-by: Thierry Reding <treding@nvidia.com>
Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/clk/tegra/clk-pll-out.c  | 26 ++++++++++++++++++++++++++
 drivers/clk/tegra/clk-tegra210.c |  3 ++-
 drivers/clk/tegra/clk.h          |  9 +++++++++
 3 files changed, 37 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/tegra/clk-pll-out.c b/drivers/clk/tegra/clk-pll-out.c
index 35f2bf00e1e6..312a3e8ef398 100644
--- a/drivers/clk/tegra/clk-pll-out.c
+++ b/drivers/clk/tegra/clk-pll-out.c
@@ -69,10 +69,36 @@ static void clk_pll_out_disable(struct clk_hw *hw)
 		spin_unlock_irqrestore(pll_out->lock, flags);
 }
 
+static int tegra_clk_pll_out_save_context(struct clk_hw *hw)
+{
+	struct tegra_clk_pll_out *pll_out = to_clk_pll_out(hw);
+
+	if (pll_out->flags & TEGRA_PLLRE_OUT)
+		pll_out->pllout_ctx = readl_relaxed(pll_out->reg);
+
+	return 0;
+}
+
+static void tegra_clk_pll_out_restore_context(struct clk_hw *hw)
+{
+	struct tegra_clk_pll_out *pll_out = to_clk_pll_out(hw);
+
+	if (pll_out->flags & TEGRA_PLLRE_OUT) {
+		writel_relaxed(pll_out->pllout_ctx, pll_out->reg);
+	} else {
+		if (!__clk_get_enable_count(hw->clk))
+			clk_pll_out_disable(hw);
+		else
+			clk_pll_out_enable(hw);
+	}
+}
+
 const struct clk_ops tegra_clk_pll_out_ops = {
 	.is_enabled = clk_pll_out_is_enabled,
 	.enable = clk_pll_out_enable,
 	.disable = clk_pll_out_disable,
+	.save_context = tegra_clk_pll_out_save_context,
+	.restore_context = tegra_clk_pll_out_restore_context,
 };
 
 struct clk *tegra_clk_register_pll_out(const char *name,
diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c
index df172d5772d7..4721ee030d1c 100644
--- a/drivers/clk/tegra/clk-tegra210.c
+++ b/drivers/clk/tegra/clk-tegra210.c
@@ -3200,7 +3200,8 @@ static void __init tegra210_pll_init(void __iomem *clk_base,
 					 8, 8, 1, NULL);
 	clk = tegra_clk_register_pll_out("pll_re_out1", "pll_re_out1_div",
 					 clk_base + PLLRE_OUT1, 1, 0,
-					 CLK_SET_RATE_PARENT, 0, NULL);
+					 CLK_SET_RATE_PARENT, TEGRA_PLLRE_OUT,
+					 NULL);
 	clks[TEGRA210_CLK_PLL_RE_OUT1] = clk;
 
 	/* PLLE */
diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
index 905bf1096558..230c05d8eef0 100644
--- a/drivers/clk/tegra/clk.h
+++ b/drivers/clk/tegra/clk.h
@@ -437,6 +437,12 @@ struct clk *tegra_clk_register_pllu_tegra210(const char *name,
  * @rst_bit_idx:	bit to reset PLL divider
  * @lock:		register lock
  * @flags:		hardware-specific flags
+ * @pllout_ctx:		pllout context to save and restore during suspend
+ *			and resume
+ *
+ * Flags:
+ * TEGRA_PLLRE_OUT - This flag indicates that it is PLLRE_OUT and is used to
+ *		     identify PLLRE_OUT during clk_pll_out save and restore.
  */
 struct tegra_clk_pll_out {
 	struct clk_hw	hw;
@@ -445,8 +451,11 @@ struct tegra_clk_pll_out {
 	u8		rst_bit_idx;
 	spinlock_t	*lock;
 	u8		flags;
+	u32		pllout_ctx;
 };
 
+#define TEGRA_PLLRE_OUT BIT(0)
+
 #define to_clk_pll_out(_hw) container_of(_hw, struct tegra_clk_pll_out, hw)
 
 extern const struct clk_ops tegra_clk_pll_out_ops;
-- 
2.7.4

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

* [PATCH v7 05/20] clk: tegra: pll: Save and restore pll context
  2019-07-31  0:20 [PATCH v7 00/20] SC7 entry and exit support for Tegra210 Sowjanya Komatineni
                   ` (3 preceding siblings ...)
  2019-07-31  0:20 ` [PATCH v7 04/20] clk: tegra: pllout: Save and restore pllout context Sowjanya Komatineni
@ 2019-07-31  0:20 ` Sowjanya Komatineni
  2019-07-31  0:20 ` [PATCH v7 06/20] clk: tegra: Support for OSC context save and restore Sowjanya Komatineni
                   ` (14 subsequent siblings)
  19 siblings, 0 replies; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-07-31  0:20 UTC (permalink / raw)
  To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
	linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, skomatineni, linux-tegra, linux-kernel,
	mperttunen, spatra, robh+dt, digetx, devicetree

This patch implements save and restore of PLL context.

During system suspend, core power goes off and looses the settings
of the Tegra CAR controller registers.

So during suspend entry pll context is stored and on resume it is
restored back along with its state.

Acked-by: Thierry Reding <treding@nvidia.com>
Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/clk/tegra/clk-pll.c | 112 +++++++++++++++++++++++++++++++-------------
 drivers/clk/tegra/clk.h     |   4 ++
 2 files changed, 84 insertions(+), 32 deletions(-)

diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c
index 1583f5fc992f..344605135a70 100644
--- a/drivers/clk/tegra/clk-pll.c
+++ b/drivers/clk/tegra/clk-pll.c
@@ -1008,6 +1008,50 @@ static unsigned long clk_plle_recalc_rate(struct clk_hw *hw,
 	return rate;
 }
 
+static int tegra_clk_pll_save_context(struct clk_hw *hw)
+{
+	struct tegra_clk_pll *pll = to_clk_pll(hw);
+	u32 val = 0;
+
+	if (pll->params->flags & TEGRA_PLLMB)
+		val = pll_readl_base(pll);
+	else if (pll->params->flags & TEGRA_PLLRE)
+		val = pll_readl_base(pll) & divp_mask_shifted(pll);
+
+	pll->pllbase_ctx = val;
+	return 0;
+}
+
+static void tegra_clk_pll_restore_context(struct clk_hw *hw)
+{
+	struct tegra_clk_pll *pll = to_clk_pll(hw);
+	struct clk_hw *parent = clk_hw_get_parent(hw);
+	unsigned long parent_rate = clk_hw_get_rate(parent);
+	unsigned long rate = clk_hw_get_rate(hw);
+	u32 val;
+
+	if (clk_pll_is_enabled(hw))
+		return;
+
+	if (pll->params->flags & TEGRA_PLLMB) {
+		pll_writel_base(pll->pllbase_ctx, pll);
+	} else if (pll->params->flags & TEGRA_PLLRE) {
+		val = pll_readl_base(pll);
+		val &= ~(divp_mask_shifted(pll));
+		pll_writel_base(pll->pllbase_ctx | val, pll);
+	}
+
+	if (pll->params->set_defaults)
+		pll->params->set_defaults(pll);
+
+	clk_pll_set_rate(hw, rate, parent_rate);
+
+	if (!__clk_get_enable_count(hw->clk))
+		clk_pll_disable(hw);
+	else
+		clk_pll_enable(hw);
+}
+
 const struct clk_ops tegra_clk_pll_ops = {
 	.is_enabled = clk_pll_is_enabled,
 	.enable = clk_pll_enable,
@@ -1015,6 +1059,8 @@ const struct clk_ops tegra_clk_pll_ops = {
 	.recalc_rate = clk_pll_recalc_rate,
 	.round_rate = clk_pll_round_rate,
 	.set_rate = clk_pll_set_rate,
+	.save_context = tegra_clk_pll_save_context,
+	.restore_context = tegra_clk_pll_restore_context,
 };
 
 const struct clk_ops tegra_clk_plle_ops = {
@@ -1802,6 +1848,27 @@ static int clk_pllu_tegra114_enable(struct clk_hw *hw)
 
 	return ret;
 }
+
+static void _clk_plle_tegra_init_parent(struct tegra_clk_pll *pll)
+{
+	u32 val, val_aux;
+
+	/* ensure parent is set to pll_ref */
+	val = pll_readl_base(pll);
+	val_aux = pll_readl(pll->params->aux_reg, pll);
+
+	if (val & PLL_BASE_ENABLE) {
+		if ((val_aux & PLLE_AUX_PLLRE_SEL) ||
+		    (val_aux & PLLE_AUX_PLLP_SEL))
+			WARN(1, "pll_e enabled with unsupported parent %s\n",
+			     (val_aux & PLLE_AUX_PLLP_SEL) ? "pllp_out0" :
+			     "pll_re_vco");
+	} else {
+		val_aux &= ~(PLLE_AUX_PLLRE_SEL | PLLE_AUX_PLLP_SEL);
+		pll_writel(val_aux, pll->params->aux_reg, pll);
+		fence_udelay(1, pll->clk_base);
+	}
+}
 #endif
 
 static struct tegra_clk_pll *_tegra_init_pll(void __iomem *clk_base,
@@ -2214,27 +2281,12 @@ struct clk *tegra_clk_register_plle_tegra114(const char *name,
 {
 	struct tegra_clk_pll *pll;
 	struct clk *clk;
-	u32 val, val_aux;
 
 	pll = _tegra_init_pll(clk_base, NULL, pll_params, lock);
 	if (IS_ERR(pll))
 		return ERR_CAST(pll);
 
-	/* ensure parent is set to pll_re_vco */
-
-	val = pll_readl_base(pll);
-	val_aux = pll_readl(pll_params->aux_reg, pll);
-
-	if (val & PLL_BASE_ENABLE) {
-		if ((val_aux & PLLE_AUX_PLLRE_SEL) ||
-			(val_aux & PLLE_AUX_PLLP_SEL))
-			WARN(1, "pll_e enabled with unsupported parent %s\n",
-			  (val_aux & PLLE_AUX_PLLP_SEL) ? "pllp_out0" :
-					"pll_re_vco");
-	} else {
-		val_aux &= ~(PLLE_AUX_PLLRE_SEL | PLLE_AUX_PLLP_SEL);
-		pll_writel(val_aux, pll_params->aux_reg, pll);
-	}
+	_clk_plle_tegra_init_parent(pll);
 
 	clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
 				      &tegra_clk_plle_tegra114_ops);
@@ -2276,6 +2328,8 @@ static const struct clk_ops tegra_clk_pllss_ops = {
 	.recalc_rate = clk_pll_recalc_rate,
 	.round_rate = clk_pll_ramp_round_rate,
 	.set_rate = clk_pllxc_set_rate,
+	.save_context = tegra_clk_pll_save_context,
+	.restore_context = tegra_clk_pll_restore_context,
 };
 
 struct clk *tegra_clk_register_pllss(const char *name, const char *parent_name,
@@ -2375,6 +2429,7 @@ struct clk *tegra_clk_register_pllre_tegra210(const char *name,
 		pll_params->vco_min = pll_params->adjust_vco(pll_params,
 							     parent_rate);
 
+	pll_params->flags |= TEGRA_PLLRE;
 	pll = _tegra_init_pll(clk_base, pmc, pll_params, lock);
 	if (IS_ERR(pll))
 		return ERR_CAST(pll);
@@ -2520,11 +2575,19 @@ static void clk_plle_tegra210_disable(struct clk_hw *hw)
 		spin_unlock_irqrestore(pll->lock, flags);
 }
 
+static void tegra_clk_plle_t210_restore_context(struct clk_hw *hw)
+{
+	struct tegra_clk_pll *pll = to_clk_pll(hw);
+
+	_clk_plle_tegra_init_parent(pll);
+}
+
 static const struct clk_ops tegra_clk_plle_tegra210_ops = {
 	.is_enabled =  clk_plle_tegra210_is_enabled,
 	.enable = clk_plle_tegra210_enable,
 	.disable = clk_plle_tegra210_disable,
 	.recalc_rate = clk_pll_recalc_rate,
+	.restore_context = tegra_clk_plle_t210_restore_context,
 };
 
 struct clk *tegra_clk_register_plle_tegra210(const char *name,
@@ -2535,27 +2598,12 @@ struct clk *tegra_clk_register_plle_tegra210(const char *name,
 {
 	struct tegra_clk_pll *pll;
 	struct clk *clk;
-	u32 val, val_aux;
 
 	pll = _tegra_init_pll(clk_base, NULL, pll_params, lock);
 	if (IS_ERR(pll))
 		return ERR_CAST(pll);
 
-	/* ensure parent is set to pll_re_vco */
-
-	val = pll_readl_base(pll);
-	val_aux = pll_readl(pll_params->aux_reg, pll);
-
-	if (val & PLLE_BASE_ENABLE) {
-		if ((val_aux & PLLE_AUX_PLLRE_SEL) ||
-			(val_aux & PLLE_AUX_PLLP_SEL))
-			WARN(1, "pll_e enabled with unsupported parent %s\n",
-			  (val_aux & PLLE_AUX_PLLP_SEL) ? "pllp_out0" :
-					"pll_re_vco");
-	} else {
-		val_aux &= ~(PLLE_AUX_PLLRE_SEL | PLLE_AUX_PLLP_SEL);
-		pll_writel(val_aux, pll_params->aux_reg, pll);
-	}
+	_clk_plle_tegra_init_parent(pll);
 
 	clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
 				      &tegra_clk_plle_tegra210_ops);
diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
index 230c05d8eef0..f1ef6ae8c979 100644
--- a/drivers/clk/tegra/clk.h
+++ b/drivers/clk/tegra/clk.h
@@ -233,6 +233,7 @@ struct tegra_clk_pll;
  * TEGRA_PLLMB - PLLMB has should be treated similar to PLLM. This
  *     flag indicated that it is PLLMB.
  * TEGRA_PLL_VCO_OUT - Used to indicate that the PLL has a VCO output
+ * TEGRA_PLLRE - Used to indicate that it is PLLRE
  */
 struct tegra_clk_pll_params {
 	unsigned long	input_min;
@@ -299,6 +300,7 @@ struct tegra_clk_pll_params {
 #define TEGRA_MDIV_NEW BIT(11)
 #define TEGRA_PLLMB BIT(12)
 #define TEGRA_PLL_VCO_OUT BIT(13)
+#define TEGRA_PLLRE BIT(14)
 
 /**
  * struct tegra_clk_pll - Tegra PLL clock
@@ -308,6 +310,7 @@ struct tegra_clk_pll_params {
  * @pmc:	address of PMC, required to read override bits
  * @lock:	register lock
  * @params:	PLL parameters
+ * @pllbase_ctx: pll base register value during suspend and resume
  */
 struct tegra_clk_pll {
 	struct clk_hw	hw;
@@ -315,6 +318,7 @@ struct tegra_clk_pll {
 	void __iomem	*pmc;
 	spinlock_t	*lock;
 	struct tegra_clk_pll_params	*params;
+	u32	pllbase_ctx;
 };
 
 #define to_clk_pll(_hw) container_of(_hw, struct tegra_clk_pll, hw)
-- 
2.7.4

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

* [PATCH v7 06/20] clk: tegra: Support for OSC context save and restore
  2019-07-31  0:20 [PATCH v7 00/20] SC7 entry and exit support for Tegra210 Sowjanya Komatineni
                   ` (4 preceding siblings ...)
  2019-07-31  0:20 ` [PATCH v7 05/20] clk: tegra: pll: Save and restore pll context Sowjanya Komatineni
@ 2019-07-31  0:20 ` Sowjanya Komatineni
  2019-07-31 11:11   ` Dmitry Osipenko
  2019-07-31  0:20 ` [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support Sowjanya Komatineni
                   ` (13 subsequent siblings)
  19 siblings, 1 reply; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-07-31  0:20 UTC (permalink / raw)
  To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
	linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, skomatineni, linux-tegra, linux-kernel,
	mperttunen, spatra, robh+dt, digetx, devicetree

This patch adds support for saving OSC clock frequency and the
drive-strength during OSC clock init and creates an API to restore
OSC control register value from the saved context.

This API is invoked by Tegra210 clock driver during system resume
to restore the  OSC clock settings.

Acked-by: Thierry Reding <treding@nvidia.com>
Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/clk/tegra/clk-tegra-fixed.c | 15 +++++++++++++++
 drivers/clk/tegra/clk.h             |  1 +
 2 files changed, 16 insertions(+)

diff --git a/drivers/clk/tegra/clk-tegra-fixed.c b/drivers/clk/tegra/clk-tegra-fixed.c
index 8d91b2b191cf..7c6c8abfcde6 100644
--- a/drivers/clk/tegra/clk-tegra-fixed.c
+++ b/drivers/clk/tegra/clk-tegra-fixed.c
@@ -17,6 +17,10 @@
 #define OSC_CTRL			0x50
 #define OSC_CTRL_OSC_FREQ_SHIFT		28
 #define OSC_CTRL_PLL_REF_DIV_SHIFT	26
+#define OSC_CTRL_MASK			(0x3f2 |	\
+					(0xf << OSC_CTRL_OSC_FREQ_SHIFT))
+
+static u32 osc_ctrl_ctx;
 
 int __init tegra_osc_clk_init(void __iomem *clk_base, struct tegra_clk *clks,
 			      unsigned long *input_freqs, unsigned int num,
@@ -29,6 +33,7 @@ int __init tegra_osc_clk_init(void __iomem *clk_base, struct tegra_clk *clks,
 	unsigned osc_idx;
 
 	val = readl_relaxed(clk_base + OSC_CTRL);
+	osc_ctrl_ctx = val & OSC_CTRL_MASK;
 	osc_idx = val >> OSC_CTRL_OSC_FREQ_SHIFT;
 
 	if (osc_idx < num)
@@ -96,3 +101,13 @@ void __init tegra_fixed_clk_init(struct tegra_clk *tegra_clks)
 		*dt_clk = clk;
 	}
 }
+
+void tegra_clk_osc_resume(void __iomem *clk_base)
+{
+	u32 val;
+
+	val = readl_relaxed(clk_base + OSC_CTRL) & ~OSC_CTRL_MASK;
+	val |= osc_ctrl_ctx;
+	writel_relaxed(val, clk_base + OSC_CTRL);
+	fence_udelay(2, clk_base);
+}
diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
index f1ef6ae8c979..abba6d8a04cd 100644
--- a/drivers/clk/tegra/clk.h
+++ b/drivers/clk/tegra/clk.h
@@ -842,6 +842,7 @@ u16 tegra_pll_get_fixed_mdiv(struct clk_hw *hw, unsigned long input_rate);
 int tegra_pll_p_div_to_hw(struct tegra_clk_pll *pll, u8 p_div);
 int div_frac_get(unsigned long rate, unsigned parent_rate, u8 width,
 		 u8 frac_width, u8 flags);
+void tegra_clk_osc_resume(void __iomem *clk_base);
 
 
 /* Combined read fence with delay */
-- 
2.7.4

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

* [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-07-31  0:20 [PATCH v7 00/20] SC7 entry and exit support for Tegra210 Sowjanya Komatineni
                   ` (5 preceding siblings ...)
  2019-07-31  0:20 ` [PATCH v7 06/20] clk: tegra: Support for OSC context save and restore Sowjanya Komatineni
@ 2019-07-31  0:20 ` Sowjanya Komatineni
  2019-07-31  9:50   ` Dmitry Osipenko
  2019-08-02 12:32   ` Dmitry Osipenko
  2019-07-31  0:20 ` [PATCH v7 08/20] clk: tegra: clk-super: Fix to enable PLLP branches to CPU Sowjanya Komatineni
                   ` (12 subsequent siblings)
  19 siblings, 2 replies; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-07-31  0:20 UTC (permalink / raw)
  To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
	linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, skomatineni, linux-tegra, linux-kernel,
	mperttunen, spatra, robh+dt, digetx, devicetree

This patch implements save and restore context for peripheral fixed
clock ops, peripheral gate clock ops, sdmmc mux clock ops, and
peripheral clock ops.

During system suspend, core power goes off and looses the settings
of the Tegra CAR controller registers.

So during suspend entry clock and reset state of peripherals is saved
and on resume they are restored to have clocks back to same rate and
state as before suspend.

Acked-by: Thierry Reding <treding@nvidia.com>
Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/clk/tegra/clk-periph-fixed.c | 33 ++++++++++++++++++++++++++++++++
 drivers/clk/tegra/clk-periph-gate.c  | 34 +++++++++++++++++++++++++++++++++
 drivers/clk/tegra/clk-periph.c       | 37 ++++++++++++++++++++++++++++++++++++
 drivers/clk/tegra/clk-sdmmc-mux.c    | 28 +++++++++++++++++++++++++++
 drivers/clk/tegra/clk.h              |  6 ++++++
 5 files changed, 138 insertions(+)

diff --git a/drivers/clk/tegra/clk-periph-fixed.c b/drivers/clk/tegra/clk-periph-fixed.c
index c088e7a280df..21b24530fa00 100644
--- a/drivers/clk/tegra/clk-periph-fixed.c
+++ b/drivers/clk/tegra/clk-periph-fixed.c
@@ -60,11 +60,44 @@ tegra_clk_periph_fixed_recalc_rate(struct clk_hw *hw,
 	return (unsigned long)rate;
 }
 
+static int tegra_clk_periph_fixed_save_context(struct clk_hw *hw)
+{
+	struct tegra_clk_periph_fixed *fixed = to_tegra_clk_periph_fixed(hw);
+	u32 mask = 1 << (fixed->num % 32);
+
+	fixed->enb_ctx = readl_relaxed(fixed->base + fixed->regs->enb_reg) &
+			 mask;
+	fixed->rst_ctx = readl_relaxed(fixed->base + fixed->regs->rst_reg) &
+			 mask;
+
+	return 0;
+}
+
+static void tegra_clk_periph_fixed_restore_context(struct clk_hw *hw)
+{
+	struct tegra_clk_periph_fixed *fixed = to_tegra_clk_periph_fixed(hw);
+	u32 mask = 1 << (fixed->num % 32);
+
+	if (fixed->enb_ctx)
+		writel_relaxed(mask, fixed->base + fixed->regs->enb_set_reg);
+	else
+		writel_relaxed(mask, fixed->base + fixed->regs->enb_clr_reg);
+
+	udelay(2);
+
+	if (!fixed->rst_ctx) {
+		udelay(5); /* reset propogation delay */
+		writel_relaxed(mask, fixed->base + fixed->regs->rst_reg);
+	}
+}
+
 static const struct clk_ops tegra_clk_periph_fixed_ops = {
 	.is_enabled = tegra_clk_periph_fixed_is_enabled,
 	.enable = tegra_clk_periph_fixed_enable,
 	.disable = tegra_clk_periph_fixed_disable,
 	.recalc_rate = tegra_clk_periph_fixed_recalc_rate,
+	.save_context = tegra_clk_periph_fixed_save_context,
+	.restore_context = tegra_clk_periph_fixed_restore_context,
 };
 
 struct clk *tegra_clk_register_periph_fixed(const char *name,
diff --git a/drivers/clk/tegra/clk-periph-gate.c b/drivers/clk/tegra/clk-periph-gate.c
index 4b31beefc9fc..6ba5b08e0787 100644
--- a/drivers/clk/tegra/clk-periph-gate.c
+++ b/drivers/clk/tegra/clk-periph-gate.c
@@ -25,6 +25,8 @@ static DEFINE_SPINLOCK(periph_ref_lock);
 
 #define read_rst(gate) \
 	readl_relaxed(gate->clk_base + (gate->regs->rst_reg))
+#define write_rst_set(val, gate) \
+	writel_relaxed(val, gate->clk_base + (gate->regs->rst_set_reg))
 #define write_rst_clr(val, gate) \
 	writel_relaxed(val, gate->clk_base + (gate->regs->rst_clr_reg))
 
@@ -110,10 +112,42 @@ static void clk_periph_disable(struct clk_hw *hw)
 	spin_unlock_irqrestore(&periph_ref_lock, flags);
 }
 
+static int clk_periph_gate_save_context(struct clk_hw *hw)
+{
+	struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
+
+	gate->clk_state_ctx = read_enb(gate) & periph_clk_to_bit(gate);
+	gate->rst_state_ctx = read_rst(gate) & periph_clk_to_bit(gate);
+
+	return 0;
+}
+
+static void clk_periph_gate_restore_context(struct clk_hw *hw)
+{
+	struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
+
+	if (gate->clk_state_ctx)
+		write_enb_set(periph_clk_to_bit(gate), gate);
+	else
+		write_enb_clr(periph_clk_to_bit(gate), gate);
+
+	udelay(5);
+
+	if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
+	    !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
+		if (gate->rst_state_ctx)
+			write_rst_set(periph_clk_to_bit(gate), gate);
+		else
+			write_rst_clr(periph_clk_to_bit(gate), gate);
+	}
+}
+
 const struct clk_ops tegra_clk_periph_gate_ops = {
 	.is_enabled = clk_periph_is_enabled,
 	.enable = clk_periph_enable,
 	.disable = clk_periph_disable,
+	.save_context = clk_periph_gate_save_context,
+	.restore_context = clk_periph_gate_restore_context,
 };
 
 struct clk *tegra_clk_register_periph_gate(const char *name,
diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c
index 58437da25156..06fb62955768 100644
--- a/drivers/clk/tegra/clk-periph.c
+++ b/drivers/clk/tegra/clk-periph.c
@@ -99,6 +99,37 @@ static void clk_periph_disable(struct clk_hw *hw)
 	gate_ops->disable(gate_hw);
 }
 
+static int clk_periph_save_context(struct clk_hw *hw)
+{
+	struct tegra_clk_periph *periph = to_clk_periph(hw);
+	const struct clk_ops *gate_ops = periph->gate_ops;
+	struct clk_hw *gate_hw = &periph->gate.hw;
+
+	if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
+		gate_ops->save_context(gate_hw);
+
+	periph->parent_ctx = clk_periph_get_parent(hw);
+
+	return 0;
+}
+
+static void clk_periph_restore_context(struct clk_hw *hw)
+{
+	struct tegra_clk_periph *periph = to_clk_periph(hw);
+	const struct clk_ops *gate_ops = periph->gate_ops;
+	struct clk_hw *gate_hw = &periph->gate.hw;
+	const struct clk_ops *div_ops = periph->div_ops;
+	struct clk_hw *div_hw = &periph->divider.hw;
+
+	clk_periph_set_parent(hw, periph->parent_ctx);
+
+	if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
+		div_ops->restore_context(div_hw);
+
+	if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
+		gate_ops->restore_context(gate_hw);
+}
+
 const struct clk_ops tegra_clk_periph_ops = {
 	.get_parent = clk_periph_get_parent,
 	.set_parent = clk_periph_set_parent,
@@ -108,6 +139,8 @@ const struct clk_ops tegra_clk_periph_ops = {
 	.is_enabled = clk_periph_is_enabled,
 	.enable = clk_periph_enable,
 	.disable = clk_periph_disable,
+	.save_context = clk_periph_save_context,
+	.restore_context = clk_periph_restore_context,
 };
 
 static const struct clk_ops tegra_clk_periph_nodiv_ops = {
@@ -116,6 +149,8 @@ static const struct clk_ops tegra_clk_periph_nodiv_ops = {
 	.is_enabled = clk_periph_is_enabled,
 	.enable = clk_periph_enable,
 	.disable = clk_periph_disable,
+	.save_context = clk_periph_save_context,
+	.restore_context = clk_periph_restore_context,
 };
 
 static const struct clk_ops tegra_clk_periph_no_gate_ops = {
@@ -124,6 +159,8 @@ static const struct clk_ops tegra_clk_periph_no_gate_ops = {
 	.recalc_rate = clk_periph_recalc_rate,
 	.round_rate = clk_periph_round_rate,
 	.set_rate = clk_periph_set_rate,
+	.save_context = clk_periph_save_context,
+	.restore_context = clk_periph_restore_context,
 };
 
 static struct clk *_tegra_clk_register_periph(const char *name,
diff --git a/drivers/clk/tegra/clk-sdmmc-mux.c b/drivers/clk/tegra/clk-sdmmc-mux.c
index a5cd3e31dbae..48da9d7fea80 100644
--- a/drivers/clk/tegra/clk-sdmmc-mux.c
+++ b/drivers/clk/tegra/clk-sdmmc-mux.c
@@ -194,6 +194,32 @@ static void clk_sdmmc_mux_disable(struct clk_hw *hw)
 	gate_ops->disable(gate_hw);
 }
 
+static int clk_sdmmc_mux_save_context(struct clk_hw *hw)
+{
+	struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw);
+	const struct clk_ops *gate_ops = sdmmc_mux->gate_ops;
+	struct clk_hw *gate_hw = &sdmmc_mux->gate.hw;
+
+	sdmmc_mux->parent_ctx = clk_sdmmc_mux_get_parent(hw);
+	gate_ops->save_context(gate_hw);
+
+	return 0;
+}
+
+static void clk_sdmmc_mux_restore_context(struct clk_hw *hw)
+{
+	struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw);
+	const struct clk_ops *gate_ops = sdmmc_mux->gate_ops;
+	struct clk_hw *gate_hw = &sdmmc_mux->gate.hw;
+	struct clk_hw *parent = clk_hw_get_parent(hw);
+	unsigned long parent_rate = clk_hw_get_rate(parent);
+	unsigned long rate = clk_hw_get_rate(hw);
+
+	clk_sdmmc_mux_set_parent(hw, sdmmc_mux->parent_ctx);
+	clk_sdmmc_mux_set_rate(hw, rate, parent_rate);
+	gate_ops->restore_context(gate_hw);
+}
+
 static const struct clk_ops tegra_clk_sdmmc_mux_ops = {
 	.get_parent = clk_sdmmc_mux_get_parent,
 	.set_parent = clk_sdmmc_mux_set_parent,
@@ -203,6 +229,8 @@ static const struct clk_ops tegra_clk_sdmmc_mux_ops = {
 	.is_enabled = clk_sdmmc_mux_is_enabled,
 	.enable = clk_sdmmc_mux_enable,
 	.disable = clk_sdmmc_mux_disable,
+	.save_context = clk_sdmmc_mux_save_context,
+	.restore_context = clk_sdmmc_mux_restore_context,
 };
 
 struct clk *tegra_clk_register_sdmmc_mux_div(const char *name,
diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
index abba6d8a04cd..d61e61eebf4a 100644
--- a/drivers/clk/tegra/clk.h
+++ b/drivers/clk/tegra/clk.h
@@ -517,6 +517,8 @@ struct tegra_clk_periph_gate {
 	int			clk_num;
 	int			*enable_refcnt;
 	const struct tegra_clk_periph_regs *regs;
+	bool			clk_state_ctx;
+	bool			rst_state_ctx;
 };
 
 #define to_clk_periph_gate(_hw)					\
@@ -543,6 +545,8 @@ struct tegra_clk_periph_fixed {
 	unsigned int mul;
 	unsigned int div;
 	unsigned int num;
+	bool enb_ctx;
+	bool rst_ctx;
 };
 
 struct clk *tegra_clk_register_periph_fixed(const char *name,
@@ -575,6 +579,7 @@ struct tegra_clk_periph {
 	const struct clk_ops	*mux_ops;
 	const struct clk_ops	*div_ops;
 	const struct clk_ops	*gate_ops;
+	u8			parent_ctx;
 };
 
 #define to_clk_periph(_hw) container_of(_hw, struct tegra_clk_periph, hw)
@@ -726,6 +731,7 @@ struct tegra_sdmmc_mux {
 	const struct clk_ops	*gate_ops;
 	struct tegra_clk_periph_gate	gate;
 	u8			div_flags;
+	u8			parent_ctx;
 };
 
 #define to_clk_sdmmc_mux(_hw) container_of(_hw, struct tegra_sdmmc_mux, hw)
-- 
2.7.4

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

* [PATCH v7 08/20] clk: tegra: clk-super: Fix to enable PLLP branches to CPU
  2019-07-31  0:20 [PATCH v7 00/20] SC7 entry and exit support for Tegra210 Sowjanya Komatineni
                   ` (6 preceding siblings ...)
  2019-07-31  0:20 ` [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support Sowjanya Komatineni
@ 2019-07-31  0:20 ` Sowjanya Komatineni
  2019-07-31 10:14   ` Dmitry Osipenko
  2019-07-31  0:20 ` [PATCH v7 09/20] clk: tegra: clk-super: Add save and restore support Sowjanya Komatineni
                   ` (11 subsequent siblings)
  19 siblings, 1 reply; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-07-31  0:20 UTC (permalink / raw)
  To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
	linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, skomatineni, linux-tegra, linux-kernel,
	mperttunen, spatra, robh+dt, digetx, devicetree

This patch has a fix to enable PLLP branches to CPU before changing
the CPU clusters clock source to PLLP for Gen5 Super clock and
disables PLLP branches to CPU when not in use.

During system suspend entry and exit, CPU source will be switched
to PLLP and this needs PLLP branches to be enabled to CPU prior to
the switch.

On system resume, warmboot code enables PLLP branches to CPU and
powers up the CPU with PLLP clock source.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/clk/tegra/clk-super.c            | 14 ++++++++++++++
 drivers/clk/tegra/clk-tegra-super-gen4.c |  2 +-
 drivers/clk/tegra/clk.c                  | 14 ++++++++++++++
 drivers/clk/tegra/clk.h                  |  5 +++++
 4 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/tegra/clk-super.c b/drivers/clk/tegra/clk-super.c
index 39ef31b46df5..e2a1e95a8db7 100644
--- a/drivers/clk/tegra/clk-super.c
+++ b/drivers/clk/tegra/clk-super.c
@@ -28,6 +28,9 @@
 #define super_state_to_src_shift(m, s) ((m->width * s))
 #define super_state_to_src_mask(m) (((1 << m->width) - 1))
 
+#define CCLK_SRC_PLLP_OUT0 4
+#define CCLK_SRC_PLLP_OUT4 5
+
 static u8 clk_super_get_parent(struct clk_hw *hw)
 {
 	struct tegra_clk_super_mux *mux = to_clk_super_mux(hw);
@@ -97,12 +100,23 @@ static int clk_super_set_parent(struct clk_hw *hw, u8 index)
 		if (index == mux->div2_index)
 			index = mux->pllx_index;
 	}
+
+	/* enable PLLP branches to CPU before selecting PLLP source */
+	if ((mux->flags & TEGRA210_CPU_CLK) &&
+	    (index == CCLK_SRC_PLLP_OUT0 || index == CCLK_SRC_PLLP_OUT4))
+		tegra_clk_set_pllp_out_cpu(true);
+
 	val &= ~((super_state_to_src_mask(mux)) << shift);
 	val |= (index & (super_state_to_src_mask(mux))) << shift;
 
 	writel_relaxed(val, mux->reg);
 	udelay(2);
 
+	/* disable PLLP branches to CPU if not used */
+	if ((mux->flags & TEGRA210_CPU_CLK) &&
+	    index != CCLK_SRC_PLLP_OUT0 && index != CCLK_SRC_PLLP_OUT4)
+		tegra_clk_set_pllp_out_cpu(false);
+
 out:
 	if (mux->lock)
 		spin_unlock_irqrestore(mux->lock, flags);
diff --git a/drivers/clk/tegra/clk-tegra-super-gen4.c b/drivers/clk/tegra/clk-tegra-super-gen4.c
index cdfe7c9697e1..888d76b01c75 100644
--- a/drivers/clk/tegra/clk-tegra-super-gen4.c
+++ b/drivers/clk/tegra/clk-tegra-super-gen4.c
@@ -180,7 +180,7 @@ static void __init tegra_super_clk_init(void __iomem *clk_base,
 					gen_info->num_cclk_g_parents,
 					CLK_SET_RATE_PARENT,
 					clk_base + CCLKG_BURST_POLICY,
-					0, 4, 8, 0, NULL);
+					TEGRA210_CPU_CLK, 4, 8, 0, NULL);
 		} else {
 			clk = tegra_clk_register_super_mux("cclk_g",
 					gen_info->cclk_g_parents,
diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c
index 573e3c967ae1..eb08047fd02f 100644
--- a/drivers/clk/tegra/clk.c
+++ b/drivers/clk/tegra/clk.c
@@ -23,6 +23,7 @@
 #define CLK_OUT_ENB_W			0x364
 #define CLK_OUT_ENB_X			0x280
 #define CLK_OUT_ENB_Y			0x298
+#define CLK_ENB_PLLP_OUT_CPU		BIT(31)
 #define CLK_OUT_ENB_SET_L		0x320
 #define CLK_OUT_ENB_CLR_L		0x324
 #define CLK_OUT_ENB_SET_H		0x328
@@ -199,6 +200,19 @@ const struct tegra_clk_periph_regs *get_reg_bank(int clkid)
 	}
 }
 
+void tegra_clk_set_pllp_out_cpu(bool enable)
+{
+	u32 val;
+
+	val = readl_relaxed(clk_base + CLK_OUT_ENB_Y);
+	if (enable)
+		val |= CLK_ENB_PLLP_OUT_CPU;
+	else
+		val &= ~CLK_ENB_PLLP_OUT_CPU;
+
+	writel_relaxed(val, clk_base + CLK_OUT_ENB_Y);
+}
+
 struct clk ** __init tegra_clk_init(void __iomem *regs, int num, int banks)
 {
 	clk_base = regs;
diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
index d61e61eebf4a..f8de447f505b 100644
--- a/drivers/clk/tegra/clk.h
+++ b/drivers/clk/tegra/clk.h
@@ -687,6 +687,9 @@ struct clk *tegra_clk_register_periph_data(void __iomem *clk_base,
  * Flags:
  * TEGRA_DIVIDER_2 - LP cluster has additional divider. This flag indicates
  *     that this is LP cluster clock.
+ * TEGRA210_CPU_CLK - This flag indicates this is CPU cluster clock. To use
+ * PLLP for CPU clock source, need to enable PLLP branches to CPU by setting
+ * additional bit PLLP_OUT_CPU for gen5 super clock.
  */
 struct tegra_clk_super_mux {
 	struct clk_hw	hw;
@@ -703,6 +706,7 @@ struct tegra_clk_super_mux {
 #define to_clk_super_mux(_hw) container_of(_hw, struct tegra_clk_super_mux, hw)
 
 #define TEGRA_DIVIDER_2 BIT(0)
+#define TEGRA210_CPU_CLK BIT(1)
 
 extern const struct clk_ops tegra_clk_super_ops;
 struct clk *tegra_clk_register_super_mux(const char *name,
@@ -849,6 +853,7 @@ int tegra_pll_p_div_to_hw(struct tegra_clk_pll *pll, u8 p_div);
 int div_frac_get(unsigned long rate, unsigned parent_rate, u8 width,
 		 u8 frac_width, u8 flags);
 void tegra_clk_osc_resume(void __iomem *clk_base);
+void tegra_clk_set_pllp_out_cpu(bool enable);
 
 
 /* Combined read fence with delay */
-- 
2.7.4

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

* [PATCH v7 09/20] clk: tegra: clk-super: Add save and restore support
  2019-07-31  0:20 [PATCH v7 00/20] SC7 entry and exit support for Tegra210 Sowjanya Komatineni
                   ` (7 preceding siblings ...)
  2019-07-31  0:20 ` [PATCH v7 08/20] clk: tegra: clk-super: Fix to enable PLLP branches to CPU Sowjanya Komatineni
@ 2019-07-31  0:20 ` Sowjanya Komatineni
  2019-07-31  0:20 ` [PATCH v7 10/20] clk: tegra: clk-dfll: Add suspend and resume support Sowjanya Komatineni
                   ` (10 subsequent siblings)
  19 siblings, 0 replies; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-07-31  0:20 UTC (permalink / raw)
  To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
	linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, skomatineni, linux-tegra, linux-kernel,
	mperttunen, spatra, robh+dt, digetx, devicetree

This patch implements save and restore context for clk_super_mux
and clk_super.

During system supend, core power goes off the and context of Tegra
CAR registers is lost.

So during suspend entry, context of super clock registers are saved
through save_context clk_ops and are restored through restore_context
clk_ops to have them in same state as before suspend.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/clk/tegra/clk-super.c | 39 +++++++++++++++++++++++++++++++++++++++
 drivers/clk/tegra/clk.h       |  1 +
 2 files changed, 40 insertions(+)

diff --git a/drivers/clk/tegra/clk-super.c b/drivers/clk/tegra/clk-super.c
index e2a1e95a8db7..be0551cb5587 100644
--- a/drivers/clk/tegra/clk-super.c
+++ b/drivers/clk/tegra/clk-super.c
@@ -124,9 +124,27 @@ static int clk_super_set_parent(struct clk_hw *hw, u8 index)
 	return err;
 }
 
+static int clk_super_mux_save_context(struct clk_hw *hw)
+{
+	struct tegra_clk_super_mux *mux = to_clk_super_mux(hw);
+
+	mux->parent_index_ctx = clk_super_get_parent(hw);
+
+	return 0;
+}
+
+static void clk_super_mux_restore_context(struct clk_hw *hw)
+{
+	struct tegra_clk_super_mux *mux = to_clk_super_mux(hw);
+
+	clk_super_set_parent(hw, mux->parent_index_ctx);
+}
+
 static const struct clk_ops tegra_clk_super_mux_ops = {
 	.get_parent = clk_super_get_parent,
 	.set_parent = clk_super_set_parent,
+	.save_context = clk_super_mux_save_context,
+	.restore_context = clk_super_mux_restore_context,
 };
 
 static long clk_super_round_rate(struct clk_hw *hw, unsigned long rate,
@@ -162,12 +180,33 @@ static int clk_super_set_rate(struct clk_hw *hw, unsigned long rate,
 	return super->div_ops->set_rate(div_hw, rate, parent_rate);
 }
 
+static int clk_super_save_context(struct clk_hw *hw)
+{
+	struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
+
+	super->parent_index_ctx = clk_super_get_parent(hw);
+
+	return 0;
+}
+
+static void clk_super_restore_context(struct clk_hw *hw)
+{
+	struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
+	struct clk_hw *div_hw = &super->frac_div.hw;
+
+	clk_super_set_parent(hw, super->parent_index_ctx);
+
+	super->div_ops->restore_context(div_hw);
+}
+
 const struct clk_ops tegra_clk_super_ops = {
 	.get_parent = clk_super_get_parent,
 	.set_parent = clk_super_set_parent,
 	.set_rate = clk_super_set_rate,
 	.round_rate = clk_super_round_rate,
 	.recalc_rate = clk_super_recalc_rate,
+	.save_context = clk_super_save_context,
+	.restore_context = clk_super_restore_context,
 };
 
 struct clk *tegra_clk_register_super_mux(const char *name,
diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
index f8de447f505b..d397dda7c6d0 100644
--- a/drivers/clk/tegra/clk.h
+++ b/drivers/clk/tegra/clk.h
@@ -701,6 +701,7 @@ struct tegra_clk_super_mux {
 	u8		div2_index;
 	u8		pllx_index;
 	spinlock_t	*lock;
+	u8		parent_index_ctx;
 };
 
 #define to_clk_super_mux(_hw) container_of(_hw, struct tegra_clk_super_mux, hw)
-- 
2.7.4

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

* [PATCH v7 10/20] clk: tegra: clk-dfll: Add suspend and resume support
  2019-07-31  0:20 [PATCH v7 00/20] SC7 entry and exit support for Tegra210 Sowjanya Komatineni
                   ` (8 preceding siblings ...)
  2019-07-31  0:20 ` [PATCH v7 09/20] clk: tegra: clk-super: Add save and restore support Sowjanya Komatineni
@ 2019-07-31  0:20 ` Sowjanya Komatineni
  2019-07-31 10:12   ` Dmitry Osipenko
  2019-07-31  0:20 ` [PATCH v7 11/20] cpufreq: tegra124: " Sowjanya Komatineni
                   ` (9 subsequent siblings)
  19 siblings, 1 reply; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-07-31  0:20 UTC (permalink / raw)
  To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
	linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, skomatineni, linux-tegra, linux-kernel,
	mperttunen, spatra, robh+dt, digetx, devicetree

This patch implements DFLL suspend and resume operation.

During system suspend entry, CPU clock will switch CPU to safe
clock source of PLLP and disables DFLL clock output.

DFLL driver suspend confirms DFLL disable state and errors out on
being active.

DFLL is re-initialized during the DFLL driver resume as it goes
through complete reset during suspend entry.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/clk/tegra/clk-dfll.c               | 56 ++++++++++++++++++++++++++++++
 drivers/clk/tegra/clk-dfll.h               |  2 ++
 drivers/clk/tegra/clk-tegra124-dfll-fcpu.c |  1 +
 3 files changed, 59 insertions(+)

diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c
index f8688c2ddf1a..9900097ec2aa 100644
--- a/drivers/clk/tegra/clk-dfll.c
+++ b/drivers/clk/tegra/clk-dfll.c
@@ -1513,6 +1513,62 @@ static int dfll_init(struct tegra_dfll *td)
 	return ret;
 }
 
+/**
+ * tegra_dfll_suspend - check DFLL is disabled
+ * @dev: DFLL device *
+ *
+ * DFLL clock should be disabled by the CPUFreq driver. So, make
+ * sure it is disabled and disable all clocks needed by the DFLL.
+ */
+int tegra_dfll_suspend(struct device *dev)
+{
+	struct tegra_dfll *td = dev_get_drvdata(dev);
+
+	if (dfll_is_running(td)) {
+		dev_err(td->dev, "dfll is enabled while shouldn't be\n");
+		return -EBUSY;
+	}
+
+	reset_control_assert(td->dvco_rst);
+
+	return 0;
+}
+EXPORT_SYMBOL(tegra_dfll_suspend);
+
+/**
+ * tegra_dfll_resume - reinitialize DFLL on resume
+ * @dev: DFLL instance
+ *
+ * DFLL is disabled and reset during suspend and resume.
+ * So, reinitialize the DFLL IP block back for use.
+ * DFLL clock is enabled later in closed loop mode by CPUFreq
+ * driver before switching its clock source to DFLL output.
+ */
+int tegra_dfll_resume(struct device *dev)
+{
+	struct tegra_dfll *td = dev_get_drvdata(dev);
+
+	reset_control_deassert(td->dvco_rst);
+
+	pm_runtime_irq_safe(td->dev);
+	pm_runtime_get_sync(td->dev);
+
+	dfll_set_mode(td, DFLL_DISABLED);
+	dfll_set_default_params(td);
+
+	if (td->soc->init_clock_trimmers)
+		td->soc->init_clock_trimmers();
+
+	dfll_set_open_loop_config(td);
+
+	dfll_init_out_if(td);
+
+	pm_runtime_put_sync(td->dev);
+
+	return 0;
+}
+EXPORT_SYMBOL(tegra_dfll_resume);
+
 /*
  * DT data fetch
  */
diff --git a/drivers/clk/tegra/clk-dfll.h b/drivers/clk/tegra/clk-dfll.h
index 1b14ebe7268b..fb209eb5f365 100644
--- a/drivers/clk/tegra/clk-dfll.h
+++ b/drivers/clk/tegra/clk-dfll.h
@@ -42,5 +42,7 @@ int tegra_dfll_register(struct platform_device *pdev,
 struct tegra_dfll_soc_data *tegra_dfll_unregister(struct platform_device *pdev);
 int tegra_dfll_runtime_suspend(struct device *dev);
 int tegra_dfll_runtime_resume(struct device *dev);
+int tegra_dfll_suspend(struct device *dev);
+int tegra_dfll_resume(struct device *dev);
 
 #endif /* __DRIVERS_CLK_TEGRA_CLK_DFLL_H */
diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
index e84b6d52cbbd..2ac2679d696d 100644
--- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
+++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
@@ -631,6 +631,7 @@ static int tegra124_dfll_fcpu_remove(struct platform_device *pdev)
 static const struct dev_pm_ops tegra124_dfll_pm_ops = {
 	SET_RUNTIME_PM_OPS(tegra_dfll_runtime_suspend,
 			   tegra_dfll_runtime_resume, NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(tegra_dfll_suspend, tegra_dfll_resume)
 };
 
 static struct platform_driver tegra124_dfll_fcpu_driver = {
-- 
2.7.4

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

* [PATCH v7 11/20] cpufreq: tegra124: Add suspend and resume support
  2019-07-31  0:20 [PATCH v7 00/20] SC7 entry and exit support for Tegra210 Sowjanya Komatineni
                   ` (9 preceding siblings ...)
  2019-07-31  0:20 ` [PATCH v7 10/20] clk: tegra: clk-dfll: Add suspend and resume support Sowjanya Komatineni
@ 2019-07-31  0:20 ` Sowjanya Komatineni
  2019-07-31 10:23   ` Dmitry Osipenko
  2019-07-31  0:20 ` [PATCH v7 12/20] clk: tegra210: Use fence_udelay during PLLU init Sowjanya Komatineni
                   ` (8 subsequent siblings)
  19 siblings, 1 reply; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-07-31  0:20 UTC (permalink / raw)
  To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
	linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, skomatineni, linux-tegra, linux-kernel,
	mperttunen, spatra, robh+dt, digetx, devicetree

This patch adds suspend and resume pm ops for cpufreq driver.

PLLP is the safe clock source for CPU during system suspend and
resume as PLLP rate is below the CPU Fmax at Vmin.

CPUFreq driver suspend switches the CPU clock source to PLLP and
disables the DFLL clock.

During system resume, warmboot code powers up the CPU with PLLP
clock source. So CPUFreq driver resume enabled DFLL clock and
switches CPU back to DFLL clock source.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/cpufreq/tegra124-cpufreq.c | 60 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 60 insertions(+)

diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c
index 4f0c637b3b49..e979a3370988 100644
--- a/drivers/cpufreq/tegra124-cpufreq.c
+++ b/drivers/cpufreq/tegra124-cpufreq.c
@@ -6,6 +6,7 @@
 #define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
 
 #include <linux/clk.h>
+#include <linux/cpufreq.h>
 #include <linux/err.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
@@ -128,8 +129,67 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
 	return ret;
 }
 
+static int __maybe_unused tegra124_cpufreq_suspend(struct device *dev)
+{
+	struct tegra124_cpufreq_priv *priv = dev_get_drvdata(dev);
+	int err;
+
+	/*
+	 * PLLP rate 408Mhz is below the CPU Fmax at Vmin and is safe to
+	 * use during suspend and resume. So, switch the CPU clock source
+	 * to PLLP and disable DFLL.
+	 */
+	err = clk_set_parent(priv->cpu_clk, priv->pllp_clk);
+	if (err < 0) {
+		dev_err(dev, "failed to reparent to PLLP: %d\n", err);
+		return err;
+	}
+
+	/* disable DFLL clock */
+	clk_disable_unprepare(priv->dfll_clk);
+
+	return 0;
+}
+
+static int __maybe_unused tegra124_cpufreq_resume(struct device *dev)
+{
+	struct tegra124_cpufreq_priv *priv = dev_get_drvdata(dev);
+	int err;
+
+	/*
+	 * Warmboot code powers up the CPU with PLLP clock source.
+	 * Enable DFLL clock and switch CPU clock source back to DFLL.
+	 */
+	err = clk_prepare_enable(priv->dfll_clk);
+	if (err < 0) {
+		dev_err(dev, "failed to enable DFLL clock for CPU: %d\n", err);
+		goto disable_cpufreq;
+	}
+
+	err = clk_set_parent(priv->cpu_clk, priv->dfll_clk);
+	if (err < 0) {
+		dev_err(dev, "failed to reparent to DFLL clock: %d\n", err);
+		goto disable_dfll;
+	}
+
+	return 0;
+
+disable_dfll:
+	clk_disable_unprepare(priv->dfll_clk);
+disable_cpufreq:
+	disable_cpufreq();
+
+	return err;
+}
+
+static const struct dev_pm_ops tegra124_cpufreq_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(tegra124_cpufreq_suspend,
+				tegra124_cpufreq_resume)
+};
+
 static struct platform_driver tegra124_cpufreq_platdrv = {
 	.driver.name	= "cpufreq-tegra124",
+	.driver.pm	= &tegra124_cpufreq_pm_ops,
 	.probe		= tegra124_cpufreq_probe,
 };
 
-- 
2.7.4

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

* [PATCH v7 12/20] clk: tegra210: Use fence_udelay during PLLU init
  2019-07-31  0:20 [PATCH v7 00/20] SC7 entry and exit support for Tegra210 Sowjanya Komatineni
                   ` (10 preceding siblings ...)
  2019-07-31  0:20 ` [PATCH v7 11/20] cpufreq: tegra124: " Sowjanya Komatineni
@ 2019-07-31  0:20 ` Sowjanya Komatineni
  2019-07-31  0:20 ` [PATCH v7 13/20] clk: tegra210: Add suspend and resume support Sowjanya Komatineni
                   ` (7 subsequent siblings)
  19 siblings, 0 replies; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-07-31  0:20 UTC (permalink / raw)
  To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
	linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, skomatineni, linux-tegra, linux-kernel,
	mperttunen, spatra, robh+dt, digetx, devicetree

This patch uses fence_udelay rather than udelay during PLLU
initialization to ensure writes to clock registers happens before
waiting for specified delay.

Acked-by: Thierry Reding <treding@nvidia.com>
Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/clk/tegra/clk-tegra210.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c
index 4721ee030d1c..998bf60b219a 100644
--- a/drivers/clk/tegra/clk-tegra210.c
+++ b/drivers/clk/tegra/clk-tegra210.c
@@ -2841,7 +2841,7 @@ static int tegra210_enable_pllu(void)
 	reg = readl_relaxed(clk_base + pllu.params->ext_misc_reg[0]);
 	reg &= ~BIT(pllu.params->iddq_bit_idx);
 	writel_relaxed(reg, clk_base + pllu.params->ext_misc_reg[0]);
-	udelay(5);
+	fence_udelay(5, clk_base);
 
 	reg = readl_relaxed(clk_base + PLLU_BASE);
 	reg &= ~GENMASK(20, 0);
@@ -2849,7 +2849,7 @@ static int tegra210_enable_pllu(void)
 	reg |= fentry->n << 8;
 	reg |= fentry->p << 16;
 	writel(reg, clk_base + PLLU_BASE);
-	udelay(1);
+	fence_udelay(1, clk_base);
 	reg |= PLL_ENABLE;
 	writel(reg, clk_base + PLLU_BASE);
 
@@ -2895,12 +2895,12 @@ static int tegra210_init_pllu(void)
 		reg = readl_relaxed(clk_base + XUSB_PLL_CFG0);
 		reg &= ~XUSB_PLL_CFG0_PLLU_LOCK_DLY_MASK;
 		writel_relaxed(reg, clk_base + XUSB_PLL_CFG0);
-		udelay(1);
+		fence_udelay(1, clk_base);
 
 		reg = readl_relaxed(clk_base + PLLU_HW_PWRDN_CFG0);
 		reg |= PLLU_HW_PWRDN_CFG0_SEQ_ENABLE;
 		writel_relaxed(reg, clk_base + PLLU_HW_PWRDN_CFG0);
-		udelay(1);
+		fence_udelay(1, clk_base);
 
 		reg = readl_relaxed(clk_base + PLLU_BASE);
 		reg &= ~PLLU_BASE_CLKENABLE_USB;
-- 
2.7.4

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

* [PATCH v7 13/20] clk: tegra210: Add suspend and resume support
  2019-07-31  0:20 [PATCH v7 00/20] SC7 entry and exit support for Tegra210 Sowjanya Komatineni
                   ` (11 preceding siblings ...)
  2019-07-31  0:20 ` [PATCH v7 12/20] clk: tegra210: Use fence_udelay during PLLU init Sowjanya Komatineni
@ 2019-07-31  0:20 ` Sowjanya Komatineni
  2019-07-31  0:20 ` [PATCH v7 14/20] soc/tegra: pmc: Allow to support more tegras wake Sowjanya Komatineni
                   ` (6 subsequent siblings)
  19 siblings, 0 replies; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-07-31  0:20 UTC (permalink / raw)
  To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
	linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, skomatineni, linux-tegra, linux-kernel,
	mperttunen, spatra, robh+dt, digetx, devicetree

This patch adds support for clk: tegra210: suspend-resume.

All the CAR controller settings are lost on suspend when core
power goes off.

This patch has implementation for saving and restoring all PLLs
and clocks context during system suspend and resume to have the
clocks back to same state for normal operation.

Clock driver suspend and resume are registered as syscore_ops as clocks
restore need to happen before the other drivers resume to have all their
clocks back to the same state as before suspend.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/clk/tegra/clk-tegra210.c | 75 +++++++++++++++++++++++++++++++++++++---
 1 file changed, 71 insertions(+), 4 deletions(-)

diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c
index 998bf60b219a..14ea9f373a84 100644
--- a/drivers/clk/tegra/clk-tegra210.c
+++ b/drivers/clk/tegra/clk-tegra210.c
@@ -9,13 +9,13 @@
 #include <linux/clkdev.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/syscore_ops.h>
 #include <linux/delay.h>
 #include <linux/export.h>
 #include <linux/mutex.h>
 #include <linux/clk/tegra.h>
 #include <dt-bindings/clock/tegra210-car.h>
 #include <dt-bindings/reset/tegra210-car.h>
-#include <linux/iopoll.h>
 #include <linux/sizes.h>
 #include <soc/tegra/pmc.h>
 
@@ -220,11 +220,15 @@
 #define CLK_M_DIVISOR_SHIFT 2
 #define CLK_M_DIVISOR_MASK 0x3
 
+#define CLK_MASK_ARM	0x44
+#define MISC_CLK_ENB	0x48
+
 #define RST_DFLL_DVCO 0x2f4
 #define DVFS_DFLL_RESET_SHIFT 0
 
 #define CLK_RST_CONTROLLER_RST_DEV_Y_SET 0x2a8
 #define CLK_RST_CONTROLLER_RST_DEV_Y_CLR 0x2ac
+#define CPU_SOFTRST_CTRL 0x380
 
 #define LVL2_CLK_GATE_OVRA 0xf8
 #define LVL2_CLK_GATE_OVRC 0x3a0
@@ -2825,6 +2829,7 @@ static int tegra210_enable_pllu(void)
 	struct tegra_clk_pll_freq_table *fentry;
 	struct tegra_clk_pll pllu;
 	u32 reg;
+	int ret;
 
 	for (fentry = pll_u_freq_table; fentry->input_rate; fentry++) {
 		if (fentry->input_rate == pll_ref_freq)
@@ -2853,9 +2858,14 @@ static int tegra210_enable_pllu(void)
 	reg |= PLL_ENABLE;
 	writel(reg, clk_base + PLLU_BASE);
 
-	readl_relaxed_poll_timeout_atomic(clk_base + PLLU_BASE, reg,
-					  reg & PLL_BASE_LOCK, 2, 1000);
-	if (!(reg & PLL_BASE_LOCK)) {
+	/*
+	 * During clock resume syscore operation, same PLLU init and enable
+	 * routines gets invoked. So, readx_poll_timeout_atomic can't be used
+	 * here as it uses ktime_get() and timekeeping resume doesn't happen
+	 * by that time. So, using tegra210_wait_for_mask for PLL LOCK.
+	 */
+	ret = tegra210_wait_for_mask(&pllu, PLLU_BASE, PLL_BASE_LOCK);
+	if (ret) {
 		pr_err("Timed out waiting for PLL_U to lock\n");
 		return -ETIMEDOUT;
 	}
@@ -3288,6 +3298,56 @@ static void tegra210_disable_cpu_clock(u32 cpu)
 }
 
 #ifdef CONFIG_PM_SLEEP
+#define car_readl(_base, _off) readl_relaxed(clk_base + (_base) + ((_off) * 4))
+#define car_writel(_val, _base, _off) \
+		writel_relaxed(_val, clk_base + (_base) + ((_off) * 4))
+
+static u32 spare_reg_ctx, misc_clk_enb_ctx, clk_msk_arm_ctx;
+static u32 cpu_softrst_ctx[3];
+
+static int tegra210_clk_suspend(void)
+{
+	unsigned int i;
+
+	clk_save_context();
+
+	/*
+	 * Save the bootloader configured clock registers SPARE_REG0,
+	 * MISC_CLK_ENB, CLK_MASK_ARM, CPU_SOFTRST_CTRL.
+	 */
+	spare_reg_ctx = readl_relaxed(clk_base + SPARE_REG0);
+	misc_clk_enb_ctx = readl_relaxed(clk_base + MISC_CLK_ENB);
+	clk_msk_arm_ctx = readl_relaxed(clk_base + CLK_MASK_ARM);
+
+	for (i = 0; i < ARRAY_SIZE(cpu_softrst_ctx); i++)
+		cpu_softrst_ctx[i] = car_readl(CPU_SOFTRST_CTRL, i);
+
+	return 0;
+}
+
+static void tegra210_clk_resume(void)
+{
+	unsigned int i;
+
+	tegra_clk_osc_resume(clk_base);
+
+	/*
+	 * Restore the bootloader configured clock registers SPARE_REG0,
+	 * MISC_CLK_ENB, CLK_MASK_ARM, CPU_SOFTRST_CTRL from saved context.
+	 */
+	writel_relaxed(spare_reg_ctx, clk_base + SPARE_REG0);
+	writel_relaxed(misc_clk_enb_ctx, clk_base + MISC_CLK_ENB);
+	writel_relaxed(clk_msk_arm_ctx, clk_base + CLK_MASK_ARM);
+
+	for (i = 0; i < ARRAY_SIZE(cpu_softrst_ctx); i++)
+		car_writel(cpu_softrst_ctx[i], CPU_SOFTRST_CTRL, i);
+
+	fence_udelay(5, clk_base);
+
+	tegra210_init_pllu();
+	clk_restore_context();
+}
+
 static void tegra210_cpu_clock_suspend(void)
 {
 	/* switch coresite to clk_m, save off original source */
@@ -3303,6 +3363,11 @@ static void tegra210_cpu_clock_resume(void)
 }
 #endif
 
+static struct syscore_ops tegra_clk_syscore_ops = {
+	.suspend = tegra210_clk_suspend,
+	.resume = tegra210_clk_resume,
+};
+
 static struct tegra_cpu_car_ops tegra210_cpu_car_ops = {
 	.wait_for_reset	= tegra210_wait_cpu_in_reset,
 	.disable_clock	= tegra210_disable_cpu_clock,
@@ -3587,5 +3652,7 @@ static void __init tegra210_clock_init(struct device_node *np)
 	tegra210_mbist_clk_init();
 
 	tegra_cpu_car_ops = &tegra210_cpu_car_ops;
+
+	register_syscore_ops(&tegra_clk_syscore_ops);
 }
 CLK_OF_DECLARE(tegra210, "nvidia,tegra210-car", tegra210_clock_init);
-- 
2.7.4

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

* [PATCH v7 14/20] soc/tegra: pmc: Allow to support more tegras wake
  2019-07-31  0:20 [PATCH v7 00/20] SC7 entry and exit support for Tegra210 Sowjanya Komatineni
                   ` (12 preceding siblings ...)
  2019-07-31  0:20 ` [PATCH v7 13/20] clk: tegra210: Add suspend and resume support Sowjanya Komatineni
@ 2019-07-31  0:20 ` Sowjanya Komatineni
  2019-07-31  0:20 ` [PATCH v7 15/20] soc/tegra: pmc: Add pmc wake support for tegra210 Sowjanya Komatineni
                   ` (5 subsequent siblings)
  19 siblings, 0 replies; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-07-31  0:20 UTC (permalink / raw)
  To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
	linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, skomatineni, linux-tegra, linux-kernel,
	mperttunen, spatra, robh+dt, digetx, devicetree

This patch allows to create separate irq_set_wake and irq_set_type
implementations for different tegra designs PMC that has different
wake models which require difference wake registers and different
programming sequence.

AOWAKE model support is available for Tegra186 and Tegra194 only
and it resides within PMC and supports tiered wake architecture.

Tegra210 and prior tegra designs uses PMC directly to receive wake
events and coordinate the wake sequence.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/soc/tegra/pmc.c | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index 9f9c1c677cf4..91c84d0e66ae 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -226,6 +226,8 @@ struct tegra_pmc_soc {
 	void (*setup_irq_polarity)(struct tegra_pmc *pmc,
 				   struct device_node *np,
 				   bool invert);
+	int (*irq_set_wake)(struct irq_data *data, unsigned int on);
+	int (*irq_set_type)(struct irq_data *data, unsigned int type);
 
 	const char * const *reset_sources;
 	unsigned int num_reset_sources;
@@ -1920,7 +1922,7 @@ static const struct irq_domain_ops tegra_pmc_irq_domain_ops = {
 	.alloc = tegra_pmc_irq_alloc,
 };
 
-static int tegra_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
+static int tegra186_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
 {
 	struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
 	unsigned int offset, bit;
@@ -1952,7 +1954,7 @@ static int tegra_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
 	return 0;
 }
 
-static int tegra_pmc_irq_set_type(struct irq_data *data, unsigned int type)
+static int tegra186_pmc_irq_set_type(struct irq_data *data, unsigned int type)
 {
 	struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
 	u32 value;
@@ -2006,8 +2008,8 @@ static int tegra_pmc_irq_init(struct tegra_pmc *pmc)
 	pmc->irq.irq_unmask = irq_chip_unmask_parent;
 	pmc->irq.irq_eoi = irq_chip_eoi_parent;
 	pmc->irq.irq_set_affinity = irq_chip_set_affinity_parent;
-	pmc->irq.irq_set_type = tegra_pmc_irq_set_type;
-	pmc->irq.irq_set_wake = tegra_pmc_irq_set_wake;
+	pmc->irq.irq_set_type = pmc->soc->irq_set_type;
+	pmc->irq.irq_set_wake = pmc->soc->irq_set_wake;
 
 	pmc->domain = irq_domain_add_hierarchy(parent, 0, 96, pmc->dev->of_node,
 					       &tegra_pmc_irq_domain_ops, pmc);
@@ -2680,6 +2682,8 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = {
 	.regs = &tegra186_pmc_regs,
 	.init = NULL,
 	.setup_irq_polarity = tegra186_pmc_setup_irq_polarity,
+	.irq_set_wake = tegra186_pmc_irq_set_wake,
+	.irq_set_type = tegra186_pmc_irq_set_type,
 	.reset_sources = tegra186_reset_sources,
 	.num_reset_sources = ARRAY_SIZE(tegra186_reset_sources),
 	.reset_levels = tegra186_reset_levels,
-- 
2.7.4

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

* [PATCH v7 15/20] soc/tegra: pmc: Add pmc wake support for tegra210
  2019-07-31  0:20 [PATCH v7 00/20] SC7 entry and exit support for Tegra210 Sowjanya Komatineni
                   ` (13 preceding siblings ...)
  2019-07-31  0:20 ` [PATCH v7 14/20] soc/tegra: pmc: Allow to support more tegras wake Sowjanya Komatineni
@ 2019-07-31  0:20 ` Sowjanya Komatineni
  2019-07-31  0:20 ` [PATCH v7 16/20] arm64: tegra: Enable wake from deep sleep on RTC alarm Sowjanya Komatineni
                   ` (4 subsequent siblings)
  19 siblings, 0 replies; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-07-31  0:20 UTC (permalink / raw)
  To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
	linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, skomatineni, linux-tegra, linux-kernel,
	mperttunen, spatra, robh+dt, digetx, devicetree

This patch implements PMC wakeup sequence for Tegra210 and defines
common used RTC alarm wake event.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/soc/tegra/pmc.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 98 insertions(+)

diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index 91c84d0e66ae..3aa71c28a10a 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -58,6 +58,11 @@
 #define  PMC_CNTRL_SYSCLK_POLARITY	BIT(10) /* sys clk polarity */
 #define  PMC_CNTRL_MAIN_RST		BIT(4)
 
+#define PMC_WAKE_MASK			0x0c
+#define PMC_WAKE_LEVEL			0x10
+#define PMC_WAKE_STATUS			0x14
+#define PMC_SW_WAKE_STATUS		0x18
+
 #define DPD_SAMPLE			0x020
 #define  DPD_SAMPLE_ENABLE		BIT(0)
 #define  DPD_SAMPLE_DISABLE		(0 << 0)
@@ -87,6 +92,11 @@
 
 #define PMC_SCRATCH41			0x140
 
+#define PMC_WAKE2_MASK			0x160
+#define PMC_WAKE2_LEVEL			0x164
+#define PMC_WAKE2_STATUS		0x168
+#define PMC_SW_WAKE2_STATUS		0x16c
+
 #define PMC_SENSOR_CTRL			0x1b0
 #define  PMC_SENSOR_CTRL_SCRATCH_WRITE	BIT(2)
 #define  PMC_SENSOR_CTRL_ENABLE_RST	BIT(1)
@@ -1922,6 +1932,43 @@ static const struct irq_domain_ops tegra_pmc_irq_domain_ops = {
 	.alloc = tegra_pmc_irq_alloc,
 };
 
+static int tegra210_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
+{
+	struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
+	unsigned int offset, bit;
+	u32 value;
+
+	if (data->hwirq == ULONG_MAX)
+		return 0;
+
+	offset = data->hwirq / 32;
+	bit = data->hwirq % 32;
+
+	/* clear wake status */
+	tegra_pmc_writel(pmc, 0, PMC_SW_WAKE_STATUS);
+	tegra_pmc_writel(pmc, 0, PMC_SW_WAKE2_STATUS);
+
+	tegra_pmc_writel(pmc, 0, PMC_WAKE_STATUS);
+	tegra_pmc_writel(pmc, 0, PMC_WAKE2_STATUS);
+
+	/* enable PMC wake */
+	if (data->hwirq >= 32)
+		offset = PMC_WAKE2_MASK;
+	else
+		offset = PMC_WAKE_MASK;
+
+	value = tegra_pmc_readl(pmc, offset);
+
+	if (on)
+		value |= 1 << bit;
+	else
+		value &= ~(1 << bit);
+
+	tegra_pmc_writel(pmc, value, offset);
+
+	return 0;
+}
+
 static int tegra186_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
 {
 	struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
@@ -1954,6 +2001,49 @@ static int tegra186_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
 	return 0;
 }
 
+static int tegra210_pmc_irq_set_type(struct irq_data *data, unsigned int type)
+{
+	struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
+	unsigned int offset, bit;
+	u32 value;
+
+	if (data->hwirq == ULONG_MAX)
+		return 0;
+
+	offset = data->hwirq / 32;
+	bit = data->hwirq % 32;
+
+	if (data->hwirq >= 32)
+		offset = PMC_WAKE2_LEVEL;
+	else
+		offset = PMC_WAKE_LEVEL;
+
+	value = tegra_pmc_readl(pmc, offset);
+
+	switch (type) {
+	case IRQ_TYPE_EDGE_RISING:
+	case IRQ_TYPE_LEVEL_HIGH:
+		value |= 1 << bit;
+		break;
+
+	case IRQ_TYPE_EDGE_FALLING:
+	case IRQ_TYPE_LEVEL_LOW:
+		value &= ~(1 << bit);
+		break;
+
+	case IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING:
+		value ^= 1 << bit;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	tegra_pmc_writel(pmc, value, offset);
+
+	return 0;
+}
+
 static int tegra186_pmc_irq_set_type(struct irq_data *data, unsigned int type)
 {
 	struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
@@ -2540,6 +2630,10 @@ static const struct pinctrl_pin_desc tegra210_pin_descs[] = {
 	TEGRA210_IO_PAD_TABLE(TEGRA_IO_PIN_DESC)
 };
 
+static const struct tegra_wake_event tegra210_wake_events[] = {
+	TEGRA_WAKE_IRQ("rtc", 16, 2),
+};
+
 static const struct tegra_pmc_soc tegra210_pmc_soc = {
 	.num_powergates = ARRAY_SIZE(tegra210_powergates),
 	.powergates = tegra210_powergates,
@@ -2557,10 +2651,14 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = {
 	.regs = &tegra20_pmc_regs,
 	.init = tegra20_pmc_init,
 	.setup_irq_polarity = tegra20_pmc_setup_irq_polarity,
+	.irq_set_wake = tegra210_pmc_irq_set_wake,
+	.irq_set_type = tegra210_pmc_irq_set_type,
 	.reset_sources = tegra210_reset_sources,
 	.num_reset_sources = ARRAY_SIZE(tegra210_reset_sources),
 	.reset_levels = NULL,
 	.num_reset_levels = 0,
+	.num_wake_events = ARRAY_SIZE(tegra210_wake_events),
+	.wake_events = tegra210_wake_events,
 };
 
 #define TEGRA186_IO_PAD_TABLE(_pad)					     \
-- 
2.7.4

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

* [PATCH v7 16/20] arm64: tegra: Enable wake from deep sleep on RTC alarm
  2019-07-31  0:20 [PATCH v7 00/20] SC7 entry and exit support for Tegra210 Sowjanya Komatineni
                   ` (14 preceding siblings ...)
  2019-07-31  0:20 ` [PATCH v7 15/20] soc/tegra: pmc: Add pmc wake support for tegra210 Sowjanya Komatineni
@ 2019-07-31  0:20 ` Sowjanya Komatineni
  2019-07-31 11:04   ` Dmitry Osipenko
  2019-07-31  0:20 ` [PATCH v7 17/20] soc/tegra: pmc: Configure core power request polarity Sowjanya Komatineni
                   ` (3 subsequent siblings)
  19 siblings, 1 reply; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-07-31  0:20 UTC (permalink / raw)
  To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
	linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, skomatineni, linux-tegra, linux-kernel,
	mperttunen, spatra, robh+dt, digetx, devicetree

This patch updates device tree for RTC and PMC to allow system wake
from deep sleep on RTC alarm.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 arch/arm64/boot/dts/nvidia/tegra210.dtsi | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/nvidia/tegra210.dtsi b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
index 659753118e96..30a7c48385a2 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
@@ -768,7 +768,8 @@
 	rtc@7000e000 {
 		compatible = "nvidia,tegra210-rtc", "nvidia,tegra20-rtc";
 		reg = <0x0 0x7000e000 0x0 0x100>;
-		interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <16 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-parent = <&pmc>;
 		clocks = <&tegra_car TEGRA210_CLK_RTC>;
 		clock-names = "rtc";
 	};
@@ -778,6 +779,8 @@
 		reg = <0x0 0x7000e400 0x0 0x400>;
 		clocks = <&tegra_car TEGRA210_CLK_PCLK>, <&clk32k_in>;
 		clock-names = "pclk", "clk32k_in";
+		#interrupt-cells = <2>;
+		interrupt-controller;
 
 		powergates {
 			pd_audio: aud {
-- 
2.7.4

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

* [PATCH v7 17/20] soc/tegra: pmc: Configure core power request polarity
  2019-07-31  0:20 [PATCH v7 00/20] SC7 entry and exit support for Tegra210 Sowjanya Komatineni
                   ` (15 preceding siblings ...)
  2019-07-31  0:20 ` [PATCH v7 16/20] arm64: tegra: Enable wake from deep sleep on RTC alarm Sowjanya Komatineni
@ 2019-07-31  0:20 ` Sowjanya Komatineni
  2019-07-31  0:20 ` [PATCH v7 18/20] soc/tegra: pmc: Configure deep sleep control settings Sowjanya Komatineni
                   ` (2 subsequent siblings)
  19 siblings, 0 replies; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-07-31  0:20 UTC (permalink / raw)
  To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
	linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, skomatineni, linux-tegra, linux-kernel,
	mperttunen, spatra, robh+dt, digetx, devicetree

This patch configures polarity of the core power request signal
in PMC control register based on the device tree property.

PMC asserts and de-asserts power request signal based on it polarity
when it need to power-up and power-down the core rail during SC7.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/soc/tegra/pmc.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index 3aa71c28a10a..e013ada7e4e9 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -56,6 +56,7 @@
 #define  PMC_CNTRL_SIDE_EFFECT_LP0	BIT(14) /* LP0 when CPU pwr gated */
 #define  PMC_CNTRL_SYSCLK_OE		BIT(11) /* system clock enable */
 #define  PMC_CNTRL_SYSCLK_POLARITY	BIT(10) /* sys clk polarity */
+#define  PMC_CNTRL_PWRREQ_POLARITY	BIT(8)
 #define  PMC_CNTRL_MAIN_RST		BIT(4)
 
 #define PMC_WAKE_MASK			0x0c
@@ -2290,6 +2291,11 @@ static void tegra20_pmc_init(struct tegra_pmc *pmc)
 	else
 		value |= PMC_CNTRL_SYSCLK_POLARITY;
 
+	if (pmc->corereq_high)
+		value &= ~PMC_CNTRL_PWRREQ_POLARITY;
+	else
+		value |= PMC_CNTRL_PWRREQ_POLARITY;
+
 	/* configure the output polarity while the request is tristated */
 	tegra_pmc_writel(pmc, value, PMC_CNTRL);
 
-- 
2.7.4

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

* [PATCH v7 18/20] soc/tegra: pmc: Configure deep sleep control settings
  2019-07-31  0:20 [PATCH v7 00/20] SC7 entry and exit support for Tegra210 Sowjanya Komatineni
                   ` (16 preceding siblings ...)
  2019-07-31  0:20 ` [PATCH v7 17/20] soc/tegra: pmc: Configure core power request polarity Sowjanya Komatineni
@ 2019-07-31  0:20 ` Sowjanya Komatineni
  2019-07-31  0:20 ` [PATCH v7 19/20] arm64: dts: tegra210-p2180: Jetson TX1 SC7 timings Sowjanya Komatineni
  2019-07-31  0:20 ` [PATCH v7 20/20] arm64: dts: tegra210-p3450: Jetson Nano " Sowjanya Komatineni
  19 siblings, 0 replies; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-07-31  0:20 UTC (permalink / raw)
  To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
	linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, skomatineni, linux-tegra, linux-kernel,
	mperttunen, spatra, robh+dt, digetx, devicetree

Tegra210 and prior Tegra chips have deep sleep entry and wakeup related
timings which are platform specific that should be configured before
entering into deep sleep.

Below are the timing specific configurations for deep sleep entry and
wakeup.
- Core rail power-on stabilization timer
- OSC clock stabilization timer after SOC rail power is stabilized.
- Core power off time is the minimum wake delay to keep the system
  in deep sleep state irrespective of any quick wake event.

These values depends on the discharge time of regulators and turn OFF
time of the PMIC to allow the complete system to finish entering into
deep sleep state.

These values vary based on the platform design and are specified
through the device tree.

This patch has implementation to configure these timings which are must
to have for proper deep sleep and wakeup operations.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/soc/tegra/pmc.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index e013ada7e4e9..9a78d8417367 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -88,6 +88,8 @@
 
 #define PMC_CPUPWRGOOD_TIMER		0xc8
 #define PMC_CPUPWROFF_TIMER		0xcc
+#define PMC_COREPWRGOOD_TIMER		0x3c
+#define PMC_COREPWROFF_TIMER		0xe0
 
 #define PMC_PWR_DET_VALUE		0xe4
 
@@ -2277,7 +2279,7 @@ static const struct tegra_pmc_regs tegra20_pmc_regs = {
 
 static void tegra20_pmc_init(struct tegra_pmc *pmc)
 {
-	u32 value;
+	u32 value, osc, pmu, off;
 
 	/* Always enable CPU power request */
 	value = tegra_pmc_readl(pmc, PMC_CNTRL);
@@ -2303,6 +2305,15 @@ static void tegra20_pmc_init(struct tegra_pmc *pmc)
 	value = tegra_pmc_readl(pmc, PMC_CNTRL);
 	value |= PMC_CNTRL_SYSCLK_OE;
 	tegra_pmc_writel(pmc, value, PMC_CNTRL);
+
+	osc = DIV_ROUND_UP(pmc->core_osc_time * 8192, 1000000);
+	pmu = DIV_ROUND_UP(pmc->core_pmu_time * 32768, 1000000);
+	off = DIV_ROUND_UP(pmc->core_off_time * 32768, 1000000);
+	if (osc && pmu)
+		tegra_pmc_writel(pmc, ((osc << 8) & 0xff00) | (pmu & 0xff),
+				 PMC_COREPWRGOOD_TIMER);
+	if (off)
+		tegra_pmc_writel(pmc, off, PMC_COREPWROFF_TIMER);
 }
 
 static void tegra20_pmc_setup_irq_polarity(struct tegra_pmc *pmc,
-- 
2.7.4

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

* [PATCH v7 19/20] arm64: dts: tegra210-p2180: Jetson TX1 SC7 timings
  2019-07-31  0:20 [PATCH v7 00/20] SC7 entry and exit support for Tegra210 Sowjanya Komatineni
                   ` (17 preceding siblings ...)
  2019-07-31  0:20 ` [PATCH v7 18/20] soc/tegra: pmc: Configure deep sleep control settings Sowjanya Komatineni
@ 2019-07-31  0:20 ` Sowjanya Komatineni
  2019-07-31  0:20 ` [PATCH v7 20/20] arm64: dts: tegra210-p3450: Jetson Nano " Sowjanya Komatineni
  19 siblings, 0 replies; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-07-31  0:20 UTC (permalink / raw)
  To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
	linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, skomatineni, linux-tegra, linux-kernel,
	mperttunen, spatra, robh+dt, digetx, devicetree

This patch has Jetson TX1 platform specific SC7 timing configuration
in device tree.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi b/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi
index 27723829d033..cb58f79deb48 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi
@@ -279,6 +279,13 @@
 
 	pmc@7000e400 {
 		nvidia,invert-interrupt;
+		nvidia,suspend-mode = <0>;
+		nvidia,cpu-pwr-good-time = <0>;
+		nvidia,cpu-pwr-off-time = <0>;
+		nvidia,core-pwr-good-time = <4587 3876>;
+		nvidia,core-pwr-off-time = <39065>;
+		nvidia,core-power-req-active-high;
+		nvidia,sys-clock-req-active-high;
 	};
 
 	/* eMMC */
-- 
2.7.4

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

* [PATCH v7 20/20] arm64: dts: tegra210-p3450: Jetson Nano SC7 timings
  2019-07-31  0:20 [PATCH v7 00/20] SC7 entry and exit support for Tegra210 Sowjanya Komatineni
                   ` (18 preceding siblings ...)
  2019-07-31  0:20 ` [PATCH v7 19/20] arm64: dts: tegra210-p2180: Jetson TX1 SC7 timings Sowjanya Komatineni
@ 2019-07-31  0:20 ` Sowjanya Komatineni
  19 siblings, 0 replies; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-07-31  0:20 UTC (permalink / raw)
  To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
	linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, skomatineni, linux-tegra, linux-kernel,
	mperttunen, spatra, robh+dt, digetx, devicetree

This patch adds Jetson Nano platform specific SC7 timing configuration
in the device tree.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts b/arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts
index 9d17ec707bce..b525e69c172a 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts
+++ b/arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts
@@ -382,6 +382,13 @@
 
 	pmc@7000e400 {
 		nvidia,invert-interrupt;
+		nvidia,suspend-mode = <0>;
+		nvidia,cpu-pwr-good-time = <0>;
+		nvidia,cpu-pwr-off-time = <0>;
+		nvidia,core-pwr-good-time = <4587 3876>;
+		nvidia,core-pwr-off-time = <39065>;
+		nvidia,core-power-req-active-high;
+		nvidia,sys-clock-req-active-high;
 	};
 
 	hda@70030000 {
-- 
2.7.4

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-07-31  0:20 ` [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support Sowjanya Komatineni
@ 2019-07-31  9:50   ` Dmitry Osipenko
  2019-07-31 10:44     ` Dmitry Osipenko
  2019-08-02 12:32   ` Dmitry Osipenko
  1 sibling, 1 reply; 67+ messages in thread
From: Dmitry Osipenko @ 2019-07-31  9:50 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree

31.07.2019 3:20, Sowjanya Komatineni пишет:
> This patch implements save and restore context for peripheral fixed
> clock ops, peripheral gate clock ops, sdmmc mux clock ops, and
> peripheral clock ops.
> 
> During system suspend, core power goes off and looses the settings
> of the Tegra CAR controller registers.
> 
> So during suspend entry clock and reset state of peripherals is saved
> and on resume they are restored to have clocks back to same rate and
> state as before suspend.
> 
> Acked-by: Thierry Reding <treding@nvidia.com>
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> ---
>  drivers/clk/tegra/clk-periph-fixed.c | 33 ++++++++++++++++++++++++++++++++
>  drivers/clk/tegra/clk-periph-gate.c  | 34 +++++++++++++++++++++++++++++++++
>  drivers/clk/tegra/clk-periph.c       | 37 ++++++++++++++++++++++++++++++++++++
>  drivers/clk/tegra/clk-sdmmc-mux.c    | 28 +++++++++++++++++++++++++++
>  drivers/clk/tegra/clk.h              |  6 ++++++
>  5 files changed, 138 insertions(+)
> 
> diff --git a/drivers/clk/tegra/clk-periph-fixed.c b/drivers/clk/tegra/clk-periph-fixed.c
> index c088e7a280df..21b24530fa00 100644
> --- a/drivers/clk/tegra/clk-periph-fixed.c
> +++ b/drivers/clk/tegra/clk-periph-fixed.c
> @@ -60,11 +60,44 @@ tegra_clk_periph_fixed_recalc_rate(struct clk_hw *hw,
>  	return (unsigned long)rate;
>  }
>  
> +static int tegra_clk_periph_fixed_save_context(struct clk_hw *hw)
> +{
> +	struct tegra_clk_periph_fixed *fixed = to_tegra_clk_periph_fixed(hw);
> +	u32 mask = 1 << (fixed->num % 32);
> +
> +	fixed->enb_ctx = readl_relaxed(fixed->base + fixed->regs->enb_reg) &
> +			 mask;
> +	fixed->rst_ctx = readl_relaxed(fixed->base + fixed->regs->rst_reg) &
> +			 mask;
> +
> +	return 0;
> +}
> +
> +static void tegra_clk_periph_fixed_restore_context(struct clk_hw *hw)
> +{
> +	struct tegra_clk_periph_fixed *fixed = to_tegra_clk_periph_fixed(hw);
> +	u32 mask = 1 << (fixed->num % 32);
> +
> +	if (fixed->enb_ctx)
> +		writel_relaxed(mask, fixed->base + fixed->regs->enb_set_reg);
> +	else
> +		writel_relaxed(mask, fixed->base + fixed->regs->enb_clr_reg);
> +
> +	udelay(2);
> +
> +	if (!fixed->rst_ctx) {
> +		udelay(5); /* reset propogation delay */
> +		writel_relaxed(mask, fixed->base + fixed->regs->rst_reg);
> +	}
> +}
> +
>  static const struct clk_ops tegra_clk_periph_fixed_ops = {
>  	.is_enabled = tegra_clk_periph_fixed_is_enabled,
>  	.enable = tegra_clk_periph_fixed_enable,
>  	.disable = tegra_clk_periph_fixed_disable,
>  	.recalc_rate = tegra_clk_periph_fixed_recalc_rate,
> +	.save_context = tegra_clk_periph_fixed_save_context,
> +	.restore_context = tegra_clk_periph_fixed_restore_context,
>  };
>  
>  struct clk *tegra_clk_register_periph_fixed(const char *name,
> diff --git a/drivers/clk/tegra/clk-periph-gate.c b/drivers/clk/tegra/clk-periph-gate.c
> index 4b31beefc9fc..6ba5b08e0787 100644
> --- a/drivers/clk/tegra/clk-periph-gate.c
> +++ b/drivers/clk/tegra/clk-periph-gate.c
> @@ -25,6 +25,8 @@ static DEFINE_SPINLOCK(periph_ref_lock);
>  
>  #define read_rst(gate) \
>  	readl_relaxed(gate->clk_base + (gate->regs->rst_reg))
> +#define write_rst_set(val, gate) \
> +	writel_relaxed(val, gate->clk_base + (gate->regs->rst_set_reg))
>  #define write_rst_clr(val, gate) \
>  	writel_relaxed(val, gate->clk_base + (gate->regs->rst_clr_reg))
>  
> @@ -110,10 +112,42 @@ static void clk_periph_disable(struct clk_hw *hw)
>  	spin_unlock_irqrestore(&periph_ref_lock, flags);
>  }
>  
> +static int clk_periph_gate_save_context(struct clk_hw *hw)
> +{
> +	struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
> +
> +	gate->clk_state_ctx = read_enb(gate) & periph_clk_to_bit(gate);
> +	gate->rst_state_ctx = read_rst(gate) & periph_clk_to_bit(gate);
> +
> +	return 0;
> +}
> +
> +static void clk_periph_gate_restore_context(struct clk_hw *hw)
> +{
> +	struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
> +
> +	if (gate->clk_state_ctx)
> +		write_enb_set(periph_clk_to_bit(gate), gate);
> +	else
> +		write_enb_clr(periph_clk_to_bit(gate), gate);
> +
> +	udelay(5);
> +
> +	if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
> +	    !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
> +		if (gate->rst_state_ctx)
> +			write_rst_set(periph_clk_to_bit(gate), gate);
> +		else
> +			write_rst_clr(periph_clk_to_bit(gate), gate);
> +	}
> +}
> +
>  const struct clk_ops tegra_clk_periph_gate_ops = {
>  	.is_enabled = clk_periph_is_enabled,
>  	.enable = clk_periph_enable,
>  	.disable = clk_periph_disable,
> +	.save_context = clk_periph_gate_save_context,
> +	.restore_context = clk_periph_gate_restore_context,
>  };
>  
>  struct clk *tegra_clk_register_periph_gate(const char *name,
> diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c
> index 58437da25156..06fb62955768 100644
> --- a/drivers/clk/tegra/clk-periph.c
> +++ b/drivers/clk/tegra/clk-periph.c
> @@ -99,6 +99,37 @@ static void clk_periph_disable(struct clk_hw *hw)
>  	gate_ops->disable(gate_hw);
>  }
>  
> +static int clk_periph_save_context(struct clk_hw *hw)
> +{
> +	struct tegra_clk_periph *periph = to_clk_periph(hw);
> +	const struct clk_ops *gate_ops = periph->gate_ops;
> +	struct clk_hw *gate_hw = &periph->gate.hw;
> +
> +	if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
> +		gate_ops->save_context(gate_hw);
> +
> +	periph->parent_ctx = clk_periph_get_parent(hw);
> +
> +	return 0;
> +}
> +
> +static void clk_periph_restore_context(struct clk_hw *hw)
> +{
> +	struct tegra_clk_periph *periph = to_clk_periph(hw);
> +	const struct clk_ops *gate_ops = periph->gate_ops;
> +	struct clk_hw *gate_hw = &periph->gate.hw;
> +	const struct clk_ops *div_ops = periph->div_ops;
> +	struct clk_hw *div_hw = &periph->divider.hw;
> +
> +	clk_periph_set_parent(hw, periph->parent_ctx);
> +
> +	if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
> +		div_ops->restore_context(div_hw);

Could you please point to where the divider's save_context() happens?
Because I can't see it.

> +	if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
> +		gate_ops->restore_context(gate_hw);
> +}
> +
>  const struct clk_ops tegra_clk_periph_ops = {
>  	.get_parent = clk_periph_get_parent,
>  	.set_parent = clk_periph_set_parent,
> @@ -108,6 +139,8 @@ const struct clk_ops tegra_clk_periph_ops = {
>  	.is_enabled = clk_periph_is_enabled,
>  	.enable = clk_periph_enable,
>  	.disable = clk_periph_disable,
> +	.save_context = clk_periph_save_context,
> +	.restore_context = clk_periph_restore_context,
>  };
>  
>  static const struct clk_ops tegra_clk_periph_nodiv_ops = {
> @@ -116,6 +149,8 @@ static const struct clk_ops tegra_clk_periph_nodiv_ops = {
>  	.is_enabled = clk_periph_is_enabled,
>  	.enable = clk_periph_enable,
>  	.disable = clk_periph_disable,
> +	.save_context = clk_periph_save_context,
> +	.restore_context = clk_periph_restore_context,
>  };
>  
>  static const struct clk_ops tegra_clk_periph_no_gate_ops = {
> @@ -124,6 +159,8 @@ static const struct clk_ops tegra_clk_periph_no_gate_ops = {
>  	.recalc_rate = clk_periph_recalc_rate,
>  	.round_rate = clk_periph_round_rate,
>  	.set_rate = clk_periph_set_rate,
> +	.save_context = clk_periph_save_context,
> +	.restore_context = clk_periph_restore_context,
>  };
>  
>  static struct clk *_tegra_clk_register_periph(const char *name,
> diff --git a/drivers/clk/tegra/clk-sdmmc-mux.c b/drivers/clk/tegra/clk-sdmmc-mux.c
> index a5cd3e31dbae..48da9d7fea80 100644
> --- a/drivers/clk/tegra/clk-sdmmc-mux.c
> +++ b/drivers/clk/tegra/clk-sdmmc-mux.c
> @@ -194,6 +194,32 @@ static void clk_sdmmc_mux_disable(struct clk_hw *hw)
>  	gate_ops->disable(gate_hw);
>  }
>  
> +static int clk_sdmmc_mux_save_context(struct clk_hw *hw)
> +{
> +	struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw);
> +	const struct clk_ops *gate_ops = sdmmc_mux->gate_ops;
> +	struct clk_hw *gate_hw = &sdmmc_mux->gate.hw;
> +
> +	sdmmc_mux->parent_ctx = clk_sdmmc_mux_get_parent(hw);
> +	gate_ops->save_context(gate_hw);
> +
> +	return 0;
> +}
> +
> +static void clk_sdmmc_mux_restore_context(struct clk_hw *hw)
> +{
> +	struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw);
> +	const struct clk_ops *gate_ops = sdmmc_mux->gate_ops;
> +	struct clk_hw *gate_hw = &sdmmc_mux->gate.hw;
> +	struct clk_hw *parent = clk_hw_get_parent(hw);
> +	unsigned long parent_rate = clk_hw_get_rate(parent);
> +	unsigned long rate = clk_hw_get_rate(hw);
> +
> +	clk_sdmmc_mux_set_parent(hw, sdmmc_mux->parent_ctx);
> +	clk_sdmmc_mux_set_rate(hw, rate, parent_rate);
> +	gate_ops->restore_context(gate_hw);
> +}
> +
>  static const struct clk_ops tegra_clk_sdmmc_mux_ops = {
>  	.get_parent = clk_sdmmc_mux_get_parent,
>  	.set_parent = clk_sdmmc_mux_set_parent,
> @@ -203,6 +229,8 @@ static const struct clk_ops tegra_clk_sdmmc_mux_ops = {
>  	.is_enabled = clk_sdmmc_mux_is_enabled,
>  	.enable = clk_sdmmc_mux_enable,
>  	.disable = clk_sdmmc_mux_disable,
> +	.save_context = clk_sdmmc_mux_save_context,
> +	.restore_context = clk_sdmmc_mux_restore_context,
>  };
>  
>  struct clk *tegra_clk_register_sdmmc_mux_div(const char *name,
> diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
> index abba6d8a04cd..d61e61eebf4a 100644
> --- a/drivers/clk/tegra/clk.h
> +++ b/drivers/clk/tegra/clk.h
> @@ -517,6 +517,8 @@ struct tegra_clk_periph_gate {
>  	int			clk_num;
>  	int			*enable_refcnt;
>  	const struct tegra_clk_periph_regs *regs;
> +	bool			clk_state_ctx;
> +	bool			rst_state_ctx;
>  };
>  
>  #define to_clk_periph_gate(_hw)					\
> @@ -543,6 +545,8 @@ struct tegra_clk_periph_fixed {
>  	unsigned int mul;
>  	unsigned int div;
>  	unsigned int num;
> +	bool enb_ctx;
> +	bool rst_ctx;
>  };
>  
>  struct clk *tegra_clk_register_periph_fixed(const char *name,
> @@ -575,6 +579,7 @@ struct tegra_clk_periph {
>  	const struct clk_ops	*mux_ops;
>  	const struct clk_ops	*div_ops;
>  	const struct clk_ops	*gate_ops;
> +	u8			parent_ctx;
>  };
>  
>  #define to_clk_periph(_hw) container_of(_hw, struct tegra_clk_periph, hw)
> @@ -726,6 +731,7 @@ struct tegra_sdmmc_mux {
>  	const struct clk_ops	*gate_ops;
>  	struct tegra_clk_periph_gate	gate;
>  	u8			div_flags;
> +	u8			parent_ctx;
>  };
>  
>  #define to_clk_sdmmc_mux(_hw) container_of(_hw, struct tegra_sdmmc_mux, hw)
> 

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

* Re: [PATCH v7 10/20] clk: tegra: clk-dfll: Add suspend and resume support
  2019-07-31  0:20 ` [PATCH v7 10/20] clk: tegra: clk-dfll: Add suspend and resume support Sowjanya Komatineni
@ 2019-07-31 10:12   ` Dmitry Osipenko
  0 siblings, 0 replies; 67+ messages in thread
From: Dmitry Osipenko @ 2019-07-31 10:12 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree

31.07.2019 3:20, Sowjanya Komatineni пишет:
> This patch implements DFLL suspend and resume operation.
> 
> During system suspend entry, CPU clock will switch CPU to safe
> clock source of PLLP and disables DFLL clock output.
> 
> DFLL driver suspend confirms DFLL disable state and errors out on
> being active.
> 
> DFLL is re-initialized during the DFLL driver resume as it goes
> through complete reset during suspend entry.
> 
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> ---
>  drivers/clk/tegra/clk-dfll.c               | 56 ++++++++++++++++++++++++++++++
>  drivers/clk/tegra/clk-dfll.h               |  2 ++
>  drivers/clk/tegra/clk-tegra124-dfll-fcpu.c |  1 +
>  3 files changed, 59 insertions(+)
> 
> diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c
> index f8688c2ddf1a..9900097ec2aa 100644
> --- a/drivers/clk/tegra/clk-dfll.c
> +++ b/drivers/clk/tegra/clk-dfll.c
> @@ -1513,6 +1513,62 @@ static int dfll_init(struct tegra_dfll *td)
>  	return ret;
>  }
>  
> +/**
> + * tegra_dfll_suspend - check DFLL is disabled
> + * @dev: DFLL device *
> + *
> + * DFLL clock should be disabled by the CPUFreq driver. So, make
> + * sure it is disabled and disable all clocks needed by the DFLL.
> + */
> +int tegra_dfll_suspend(struct device *dev)
> +{
> +	struct tegra_dfll *td = dev_get_drvdata(dev);
> +
> +	if (dfll_is_running(td)) {
> +		dev_err(td->dev, "dfll is enabled while shouldn't be\n");
> +		return -EBUSY;
> +	}
> +
> +	reset_control_assert(td->dvco_rst);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(tegra_dfll_suspend);
> +
> +/**
> + * tegra_dfll_resume - reinitialize DFLL on resume
> + * @dev: DFLL instance
> + *
> + * DFLL is disabled and reset during suspend and resume.
> + * So, reinitialize the DFLL IP block back for use.
> + * DFLL clock is enabled later in closed loop mode by CPUFreq
> + * driver before switching its clock source to DFLL output.
> + */
> +int tegra_dfll_resume(struct device *dev)
> +{
> +	struct tegra_dfll *td = dev_get_drvdata(dev);
> +
> +	reset_control_deassert(td->dvco_rst);
> +
> +	pm_runtime_irq_safe(td->dev);

1. Interrupts are allowed here.
2. It's enough to invoke that function once during probe.
3. That function bumps runtime-enable count of the parent, which
immediately should raise some questions.

Corollary: you should remove pm_runtime_irq_safe() because it is not needed.

> +	pm_runtime_get_sync(td->dev);
> +
> +	dfll_set_mode(td, DFLL_DISABLED);
> +	dfll_set_default_params(td);
> +
> +	if (td->soc->init_clock_trimmers)
> +		td->soc->init_clock_trimmers();
> +
> +	dfll_set_open_loop_config(td);
> +
> +	dfll_init_out_if(td);
> +
> +	pm_runtime_put_sync(td->dev);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(tegra_dfll_resume);
> +
>  /*
>   * DT data fetch
>   */
> diff --git a/drivers/clk/tegra/clk-dfll.h b/drivers/clk/tegra/clk-dfll.h
> index 1b14ebe7268b..fb209eb5f365 100644
> --- a/drivers/clk/tegra/clk-dfll.h
> +++ b/drivers/clk/tegra/clk-dfll.h
> @@ -42,5 +42,7 @@ int tegra_dfll_register(struct platform_device *pdev,
>  struct tegra_dfll_soc_data *tegra_dfll_unregister(struct platform_device *pdev);
>  int tegra_dfll_runtime_suspend(struct device *dev);
>  int tegra_dfll_runtime_resume(struct device *dev);
> +int tegra_dfll_suspend(struct device *dev);
> +int tegra_dfll_resume(struct device *dev);
>  
>  #endif /* __DRIVERS_CLK_TEGRA_CLK_DFLL_H */
> diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> index e84b6d52cbbd..2ac2679d696d 100644
> --- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> +++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> @@ -631,6 +631,7 @@ static int tegra124_dfll_fcpu_remove(struct platform_device *pdev)
>  static const struct dev_pm_ops tegra124_dfll_pm_ops = {
>  	SET_RUNTIME_PM_OPS(tegra_dfll_runtime_suspend,
>  			   tegra_dfll_runtime_resume, NULL)
> +	SET_SYSTEM_SLEEP_PM_OPS(tegra_dfll_suspend, tegra_dfll_resume)
>  };
>  
>  static struct platform_driver tegra124_dfll_fcpu_driver = {
> 

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

* Re: [PATCH v7 08/20] clk: tegra: clk-super: Fix to enable PLLP branches to CPU
  2019-07-31  0:20 ` [PATCH v7 08/20] clk: tegra: clk-super: Fix to enable PLLP branches to CPU Sowjanya Komatineni
@ 2019-07-31 10:14   ` Dmitry Osipenko
  0 siblings, 0 replies; 67+ messages in thread
From: Dmitry Osipenko @ 2019-07-31 10:14 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree

31.07.2019 3:20, Sowjanya Komatineni пишет:
> This patch has a fix to enable PLLP branches to CPU before changing
> the CPU clusters clock source to PLLP for Gen5 Super clock and
> disables PLLP branches to CPU when not in use.
> 
> During system suspend entry and exit, CPU source will be switched
> to PLLP and this needs PLLP branches to be enabled to CPU prior to
> the switch.
> 
> On system resume, warmboot code enables PLLP branches to CPU and
> powers up the CPU with PLLP clock source.
> 
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> ---
>  drivers/clk/tegra/clk-super.c            | 14 ++++++++++++++
>  drivers/clk/tegra/clk-tegra-super-gen4.c |  2 +-
>  drivers/clk/tegra/clk.c                  | 14 ++++++++++++++
>  drivers/clk/tegra/clk.h                  |  5 +++++
>  4 files changed, 34 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/clk/tegra/clk-super.c b/drivers/clk/tegra/clk-super.c
> index 39ef31b46df5..e2a1e95a8db7 100644
> --- a/drivers/clk/tegra/clk-super.c
> +++ b/drivers/clk/tegra/clk-super.c
> @@ -28,6 +28,9 @@
>  #define super_state_to_src_shift(m, s) ((m->width * s))
>  #define super_state_to_src_mask(m) (((1 << m->width) - 1))
>  
> +#define CCLK_SRC_PLLP_OUT0 4
> +#define CCLK_SRC_PLLP_OUT4 5
> +
>  static u8 clk_super_get_parent(struct clk_hw *hw)
>  {
>  	struct tegra_clk_super_mux *mux = to_clk_super_mux(hw);
> @@ -97,12 +100,23 @@ static int clk_super_set_parent(struct clk_hw *hw, u8 index)
>  		if (index == mux->div2_index)
>  			index = mux->pllx_index;
>  	}
> +
> +	/* enable PLLP branches to CPU before selecting PLLP source */
> +	if ((mux->flags & TEGRA210_CPU_CLK) &&
> +	    (index == CCLK_SRC_PLLP_OUT0 || index == CCLK_SRC_PLLP_OUT4))
> +		tegra_clk_set_pllp_out_cpu(true);
> +
>  	val &= ~((super_state_to_src_mask(mux)) << shift);
>  	val |= (index & (super_state_to_src_mask(mux))) << shift;
>  
>  	writel_relaxed(val, mux->reg);
>  	udelay(2);
>  
> +	/* disable PLLP branches to CPU if not used */
> +	if ((mux->flags & TEGRA210_CPU_CLK) &&
> +	    index != CCLK_SRC_PLLP_OUT0 && index != CCLK_SRC_PLLP_OUT4)
> +		tegra_clk_set_pllp_out_cpu(false);
> +
>  out:
>  	if (mux->lock)
>  		spin_unlock_irqrestore(mux->lock, flags);
> diff --git a/drivers/clk/tegra/clk-tegra-super-gen4.c b/drivers/clk/tegra/clk-tegra-super-gen4.c
> index cdfe7c9697e1..888d76b01c75 100644
> --- a/drivers/clk/tegra/clk-tegra-super-gen4.c
> +++ b/drivers/clk/tegra/clk-tegra-super-gen4.c
> @@ -180,7 +180,7 @@ static void __init tegra_super_clk_init(void __iomem *clk_base,
>  					gen_info->num_cclk_g_parents,
>  					CLK_SET_RATE_PARENT,
>  					clk_base + CCLKG_BURST_POLICY,
> -					0, 4, 8, 0, NULL);
> +					TEGRA210_CPU_CLK, 4, 8, 0, NULL);

Don't we want a clarifying comment for cclk_lp telling why it doesn't
have the TEGRA210_CPU_CLK flag?

>  		} else {
>  			clk = tegra_clk_register_super_mux("cclk_g",
>  					gen_info->cclk_g_parents,
> diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c
> index 573e3c967ae1..eb08047fd02f 100644
> --- a/drivers/clk/tegra/clk.c
> +++ b/drivers/clk/tegra/clk.c
> @@ -23,6 +23,7 @@
>  #define CLK_OUT_ENB_W			0x364
>  #define CLK_OUT_ENB_X			0x280
>  #define CLK_OUT_ENB_Y			0x298
> +#define CLK_ENB_PLLP_OUT_CPU		BIT(31)
>  #define CLK_OUT_ENB_SET_L		0x320
>  #define CLK_OUT_ENB_CLR_L		0x324
>  #define CLK_OUT_ENB_SET_H		0x328
> @@ -199,6 +200,19 @@ const struct tegra_clk_periph_regs *get_reg_bank(int clkid)
>  	}
>  }
>  
> +void tegra_clk_set_pllp_out_cpu(bool enable)
> +{
> +	u32 val;
> +
> +	val = readl_relaxed(clk_base + CLK_OUT_ENB_Y);
> +	if (enable)
> +		val |= CLK_ENB_PLLP_OUT_CPU;
> +	else
> +		val &= ~CLK_ENB_PLLP_OUT_CPU;
> +
> +	writel_relaxed(val, clk_base + CLK_OUT_ENB_Y);
> +}
> +
>  struct clk ** __init tegra_clk_init(void __iomem *regs, int num, int banks)
>  {
>  	clk_base = regs;
> diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
> index d61e61eebf4a..f8de447f505b 100644
> --- a/drivers/clk/tegra/clk.h
> +++ b/drivers/clk/tegra/clk.h
> @@ -687,6 +687,9 @@ struct clk *tegra_clk_register_periph_data(void __iomem *clk_base,
>   * Flags:
>   * TEGRA_DIVIDER_2 - LP cluster has additional divider. This flag indicates
>   *     that this is LP cluster clock.
> + * TEGRA210_CPU_CLK - This flag indicates this is CPU cluster clock. To use
> + * PLLP for CPU clock source, need to enable PLLP branches to CPU by setting
> + * additional bit PLLP_OUT_CPU for gen5 super clock.
>   */
>  struct tegra_clk_super_mux {
>  	struct clk_hw	hw;
> @@ -703,6 +706,7 @@ struct tegra_clk_super_mux {
>  #define to_clk_super_mux(_hw) container_of(_hw, struct tegra_clk_super_mux, hw)
>  
>  #define TEGRA_DIVIDER_2 BIT(0)
> +#define TEGRA210_CPU_CLK BIT(1)
>  
>  extern const struct clk_ops tegra_clk_super_ops;
>  struct clk *tegra_clk_register_super_mux(const char *name,
> @@ -849,6 +853,7 @@ int tegra_pll_p_div_to_hw(struct tegra_clk_pll *pll, u8 p_div);
>  int div_frac_get(unsigned long rate, unsigned parent_rate, u8 width,
>  		 u8 frac_width, u8 flags);
>  void tegra_clk_osc_resume(void __iomem *clk_base);
> +void tegra_clk_set_pllp_out_cpu(bool enable);
>  
>  
>  /* Combined read fence with delay */
> 

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

* Re: [PATCH v7 11/20] cpufreq: tegra124: Add suspend and resume support
  2019-07-31  0:20 ` [PATCH v7 11/20] cpufreq: tegra124: " Sowjanya Komatineni
@ 2019-07-31 10:23   ` Dmitry Osipenko
  2019-07-31 11:14     ` Dmitry Osipenko
  0 siblings, 1 reply; 67+ messages in thread
From: Dmitry Osipenko @ 2019-07-31 10:23 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree

31.07.2019 3:20, Sowjanya Komatineni пишет:
> This patch adds suspend and resume pm ops for cpufreq driver.
> 
> PLLP is the safe clock source for CPU during system suspend and
> resume as PLLP rate is below the CPU Fmax at Vmin.
> 
> CPUFreq driver suspend switches the CPU clock source to PLLP and
> disables the DFLL clock.
> 
> During system resume, warmboot code powers up the CPU with PLLP
> clock source. So CPUFreq driver resume enabled DFLL clock and
> switches CPU back to DFLL clock source.
> 
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> ---
>  drivers/cpufreq/tegra124-cpufreq.c | 60 ++++++++++++++++++++++++++++++++++++++
>  1 file changed, 60 insertions(+)
> 
> diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c
> index 4f0c637b3b49..e979a3370988 100644
> --- a/drivers/cpufreq/tegra124-cpufreq.c
> +++ b/drivers/cpufreq/tegra124-cpufreq.c
> @@ -6,6 +6,7 @@
>  #define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
>  
>  #include <linux/clk.h>
> +#include <linux/cpufreq.h>
>  #include <linux/err.h>
>  #include <linux/init.h>
>  #include <linux/kernel.h>
> @@ -128,8 +129,67 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
>  	return ret;
>  }
>  
> +static int __maybe_unused tegra124_cpufreq_suspend(struct device *dev)
> +{
> +	struct tegra124_cpufreq_priv *priv = dev_get_drvdata(dev);
> +	int err;
> +
> +	/*
> +	 * PLLP rate 408Mhz is below the CPU Fmax at Vmin and is safe to
> +	 * use during suspend and resume. So, switch the CPU clock source
> +	 * to PLLP and disable DFLL.
> +	 */
> +	err = clk_set_parent(priv->cpu_clk, priv->pllp_clk);
> +	if (err < 0) {
> +		dev_err(dev, "failed to reparent to PLLP: %d\n", err);
> +		return err;
> +	}
> +
> +	/* disable DFLL clock */
> +	clk_disable_unprepare(priv->dfll_clk);
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused tegra124_cpufreq_resume(struct device *dev)
> +{
> +	struct tegra124_cpufreq_priv *priv = dev_get_drvdata(dev);
> +	int err;
> +
> +	/*
> +	 * Warmboot code powers up the CPU with PLLP clock source.
> +	 * Enable DFLL clock and switch CPU clock source back to DFLL.
> +	 */
> +	err = clk_prepare_enable(priv->dfll_clk);
> +	if (err < 0) {
> +		dev_err(dev, "failed to enable DFLL clock for CPU: %d\n", err);
> +		goto disable_cpufreq;
> +	}
> +
> +	err = clk_set_parent(priv->cpu_clk, priv->dfll_clk);
> +	if (err < 0) {
> +		dev_err(dev, "failed to reparent to DFLL clock: %d\n", err);
> +		goto disable_dfll;
> +	}
> +
> +	return 0;
> +
> +disable_dfll:
> +	clk_disable_unprepare(priv->dfll_clk);
> +disable_cpufreq:
> +	disable_cpufreq();
> +
> +	return err;
> +}
> +
> +static const struct dev_pm_ops tegra124_cpufreq_pm_ops = {
> +	SET_SYSTEM_SLEEP_PM_OPS(tegra124_cpufreq_suspend,
> +				tegra124_cpufreq_resume)
> +};
> +
>  static struct platform_driver tegra124_cpufreq_platdrv = {
>  	.driver.name	= "cpufreq-tegra124",
> +	.driver.pm	= &tegra124_cpufreq_pm_ops,
>  	.probe		= tegra124_cpufreq_probe,
>  };
>  
> 

Looks good,

Reviewed-by: Dmitry Osipenko <digetx@gmail.com>

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-07-31  9:50   ` Dmitry Osipenko
@ 2019-07-31 10:44     ` Dmitry Osipenko
  2019-07-31 23:09       ` Sowjanya Komatineni
  0 siblings, 1 reply; 67+ messages in thread
From: Dmitry Osipenko @ 2019-07-31 10:44 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree

31.07.2019 12:50, Dmitry Osipenko пишет:
> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>> This patch implements save and restore context for peripheral fixed
>> clock ops, peripheral gate clock ops, sdmmc mux clock ops, and
>> peripheral clock ops.
>>
>> During system suspend, core power goes off and looses the settings
>> of the Tegra CAR controller registers.
>>
>> So during suspend entry clock and reset state of peripherals is saved
>> and on resume they are restored to have clocks back to same rate and
>> state as before suspend.
>>
>> Acked-by: Thierry Reding <treding@nvidia.com>
>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>> ---
>>  drivers/clk/tegra/clk-periph-fixed.c | 33 ++++++++++++++++++++++++++++++++
>>  drivers/clk/tegra/clk-periph-gate.c  | 34 +++++++++++++++++++++++++++++++++
>>  drivers/clk/tegra/clk-periph.c       | 37 ++++++++++++++++++++++++++++++++++++
>>  drivers/clk/tegra/clk-sdmmc-mux.c    | 28 +++++++++++++++++++++++++++
>>  drivers/clk/tegra/clk.h              |  6 ++++++
>>  5 files changed, 138 insertions(+)
>>
>> diff --git a/drivers/clk/tegra/clk-periph-fixed.c b/drivers/clk/tegra/clk-periph-fixed.c
>> index c088e7a280df..21b24530fa00 100644
>> --- a/drivers/clk/tegra/clk-periph-fixed.c
>> +++ b/drivers/clk/tegra/clk-periph-fixed.c
>> @@ -60,11 +60,44 @@ tegra_clk_periph_fixed_recalc_rate(struct clk_hw *hw,
>>  	return (unsigned long)rate;
>>  }
>>  
>> +static int tegra_clk_periph_fixed_save_context(struct clk_hw *hw)
>> +{
>> +	struct tegra_clk_periph_fixed *fixed = to_tegra_clk_periph_fixed(hw);
>> +	u32 mask = 1 << (fixed->num % 32);
>> +
>> +	fixed->enb_ctx = readl_relaxed(fixed->base + fixed->regs->enb_reg) &
>> +			 mask;
>> +	fixed->rst_ctx = readl_relaxed(fixed->base + fixed->regs->rst_reg) &
>> +			 mask;
>> +
>> +	return 0;
>> +}
>> +
>> +static void tegra_clk_periph_fixed_restore_context(struct clk_hw *hw)
>> +{
>> +	struct tegra_clk_periph_fixed *fixed = to_tegra_clk_periph_fixed(hw);
>> +	u32 mask = 1 << (fixed->num % 32);
>> +
>> +	if (fixed->enb_ctx)
>> +		writel_relaxed(mask, fixed->base + fixed->regs->enb_set_reg);
>> +	else
>> +		writel_relaxed(mask, fixed->base + fixed->regs->enb_clr_reg);
>> +
>> +	udelay(2);
>> +
>> +	if (!fixed->rst_ctx) {
>> +		udelay(5); /* reset propogation delay */
>> +		writel_relaxed(mask, fixed->base + fixed->regs->rst_reg);
>> +	}
>> +}
>> +
>>  static const struct clk_ops tegra_clk_periph_fixed_ops = {
>>  	.is_enabled = tegra_clk_periph_fixed_is_enabled,
>>  	.enable = tegra_clk_periph_fixed_enable,
>>  	.disable = tegra_clk_periph_fixed_disable,
>>  	.recalc_rate = tegra_clk_periph_fixed_recalc_rate,
>> +	.save_context = tegra_clk_periph_fixed_save_context,
>> +	.restore_context = tegra_clk_periph_fixed_restore_context,
>>  };
>>  
>>  struct clk *tegra_clk_register_periph_fixed(const char *name,
>> diff --git a/drivers/clk/tegra/clk-periph-gate.c b/drivers/clk/tegra/clk-periph-gate.c
>> index 4b31beefc9fc..6ba5b08e0787 100644
>> --- a/drivers/clk/tegra/clk-periph-gate.c
>> +++ b/drivers/clk/tegra/clk-periph-gate.c
>> @@ -25,6 +25,8 @@ static DEFINE_SPINLOCK(periph_ref_lock);
>>  
>>  #define read_rst(gate) \
>>  	readl_relaxed(gate->clk_base + (gate->regs->rst_reg))
>> +#define write_rst_set(val, gate) \
>> +	writel_relaxed(val, gate->clk_base + (gate->regs->rst_set_reg))
>>  #define write_rst_clr(val, gate) \
>>  	writel_relaxed(val, gate->clk_base + (gate->regs->rst_clr_reg))
>>  
>> @@ -110,10 +112,42 @@ static void clk_periph_disable(struct clk_hw *hw)
>>  	spin_unlock_irqrestore(&periph_ref_lock, flags);
>>  }
>>  
>> +static int clk_periph_gate_save_context(struct clk_hw *hw)
>> +{
>> +	struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
>> +
>> +	gate->clk_state_ctx = read_enb(gate) & periph_clk_to_bit(gate);
>> +	gate->rst_state_ctx = read_rst(gate) & periph_clk_to_bit(gate);
>> +
>> +	return 0;
>> +}
>> +
>> +static void clk_periph_gate_restore_context(struct clk_hw *hw)
>> +{
>> +	struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
>> +
>> +	if (gate->clk_state_ctx)
>> +		write_enb_set(periph_clk_to_bit(gate), gate);
>> +	else
>> +		write_enb_clr(periph_clk_to_bit(gate), gate);
>> +
>> +	udelay(5);
>> +
>> +	if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
>> +	    !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
>> +		if (gate->rst_state_ctx)
>> +			write_rst_set(periph_clk_to_bit(gate), gate);
>> +		else
>> +			write_rst_clr(periph_clk_to_bit(gate), gate);
>> +	}
>> +}
>> +
>>  const struct clk_ops tegra_clk_periph_gate_ops = {
>>  	.is_enabled = clk_periph_is_enabled,
>>  	.enable = clk_periph_enable,
>>  	.disable = clk_periph_disable,
>> +	.save_context = clk_periph_gate_save_context,
>> +	.restore_context = clk_periph_gate_restore_context,
>>  };
>>  
>>  struct clk *tegra_clk_register_periph_gate(const char *name,
>> diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c
>> index 58437da25156..06fb62955768 100644
>> --- a/drivers/clk/tegra/clk-periph.c
>> +++ b/drivers/clk/tegra/clk-periph.c
>> @@ -99,6 +99,37 @@ static void clk_periph_disable(struct clk_hw *hw)
>>  	gate_ops->disable(gate_hw);
>>  }
>>  
>> +static int clk_periph_save_context(struct clk_hw *hw)
>> +{
>> +	struct tegra_clk_periph *periph = to_clk_periph(hw);
>> +	const struct clk_ops *gate_ops = periph->gate_ops;
>> +	struct clk_hw *gate_hw = &periph->gate.hw;
>> +
>> +	if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
>> +		gate_ops->save_context(gate_hw);
>> +
>> +	periph->parent_ctx = clk_periph_get_parent(hw);
>> +
>> +	return 0;
>> +}
>> +
>> +static void clk_periph_restore_context(struct clk_hw *hw)
>> +{
>> +	struct tegra_clk_periph *periph = to_clk_periph(hw);
>> +	const struct clk_ops *gate_ops = periph->gate_ops;
>> +	struct clk_hw *gate_hw = &periph->gate.hw;
>> +	const struct clk_ops *div_ops = periph->div_ops;
>> +	struct clk_hw *div_hw = &periph->divider.hw;
>> +
>> +	clk_periph_set_parent(hw, periph->parent_ctx);
>> +
>> +	if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
>> +		div_ops->restore_context(div_hw);
> 
> Could you please point to where the divider's save_context() happens?
> Because I can't see it.

Ah, I now see that there is no need to save the dividers context because
clk itself has enough info that is needed for the context's restoring
(like I pointed in the review to v6).

Looks like you could also implement a new clk_hw_get_parent_index()
generic helper to get the index instead of storing it manually.

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

* Re: [PATCH v7 03/20] clk: tegra: divider: Save and restore divider rate
  2019-07-31  0:20 ` [PATCH v7 03/20] clk: tegra: divider: Save and restore divider rate Sowjanya Komatineni
@ 2019-07-31 10:49   ` Dmitry Osipenko
  0 siblings, 0 replies; 67+ messages in thread
From: Dmitry Osipenko @ 2019-07-31 10:49 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree

31.07.2019 3:20, Sowjanya Komatineni пишет:
> This patch implements context restore for clock divider.
> 
> During system suspend, core power goes off and looses the settings
> of the Tegra CAR controller registers.
> 
> So on resume, clock dividers are restored back for normal operation.
> 
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> ---
>  drivers/clk/tegra/clk-divider.c | 11 +++++++++++
>  1 file changed, 11 insertions(+)
> 
> diff --git a/drivers/clk/tegra/clk-divider.c b/drivers/clk/tegra/clk-divider.c
> index e76731fb7d69..ca0de5f11f84 100644
> --- a/drivers/clk/tegra/clk-divider.c
> +++ b/drivers/clk/tegra/clk-divider.c
> @@ -109,10 +109,21 @@ static int clk_frac_div_set_rate(struct clk_hw *hw, unsigned long rate,
>  	return 0;
>  }
>  
> +static void clk_divider_restore_context(struct clk_hw *hw)
> +{
> +	struct clk_hw *parent = clk_hw_get_parent(hw);
> +	unsigned long parent_rate = clk_hw_get_rate(parent);
> +	unsigned long rate = clk_hw_get_rate(hw);
> +
> +	if (clk_frac_div_set_rate(hw, rate, parent_rate) < 0)
> +		WARN_ON(1);
> +}
> +
>  const struct clk_ops tegra_clk_frac_div_ops = {
>  	.recalc_rate = clk_frac_div_recalc_rate,
>  	.set_rate = clk_frac_div_set_rate,
>  	.round_rate = clk_frac_div_round_rate,
> +	.restore_context = clk_divider_restore_context,
>  };
>  
>  struct clk *tegra_clk_register_divider(const char *name,
> 

Reviewed-by: Dmitry Osipenko <digetx@gmail.com>

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

* Re: [PATCH v7 16/20] arm64: tegra: Enable wake from deep sleep on RTC alarm
  2019-07-31  0:20 ` [PATCH v7 16/20] arm64: tegra: Enable wake from deep sleep on RTC alarm Sowjanya Komatineni
@ 2019-07-31 11:04   ` Dmitry Osipenko
  2019-07-31 21:08     ` Sowjanya Komatineni
  0 siblings, 1 reply; 67+ messages in thread
From: Dmitry Osipenko @ 2019-07-31 11:04 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree

31.07.2019 3:20, Sowjanya Komatineni пишет:
> This patch updates device tree for RTC and PMC to allow system wake
> from deep sleep on RTC alarm.
> 
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> ---
>  arch/arm64/boot/dts/nvidia/tegra210.dtsi | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/arm64/boot/dts/nvidia/tegra210.dtsi b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
> index 659753118e96..30a7c48385a2 100644
> --- a/arch/arm64/boot/dts/nvidia/tegra210.dtsi
> +++ b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
> @@ -768,7 +768,8 @@
>  	rtc@7000e000 {
>  		compatible = "nvidia,tegra210-rtc", "nvidia,tegra20-rtc";
>  		reg = <0x0 0x7000e000 0x0 0x100>;
> -		interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
> +		interrupts = <16 IRQ_TYPE_LEVEL_HIGH>;
> +		interrupt-parent = <&pmc>;
>  		clocks = <&tegra_car TEGRA210_CLK_RTC>;
>  		clock-names = "rtc";
>  	};
> @@ -778,6 +779,8 @@
>  		reg = <0x0 0x7000e400 0x0 0x400>;
>  		clocks = <&tegra_car TEGRA210_CLK_PCLK>, <&clk32k_in>;
>  		clock-names = "pclk", "clk32k_in";
> +		#interrupt-cells = <2>;
> +		interrupt-controller;
>  
>  		powergates {
>  			pd_audio: aud {
> 

Is this a backwards-compatible change? Or it's not really worth to care
about the compatibility with older kernel versions, I'm not sure about
overall state of T210 in the upstream kernel.

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

* Re: [PATCH v7 06/20] clk: tegra: Support for OSC context save and restore
  2019-07-31  0:20 ` [PATCH v7 06/20] clk: tegra: Support for OSC context save and restore Sowjanya Komatineni
@ 2019-07-31 11:11   ` Dmitry Osipenko
  2019-07-31 21:04     ` Sowjanya Komatineni
  0 siblings, 1 reply; 67+ messages in thread
From: Dmitry Osipenko @ 2019-07-31 11:11 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree

31.07.2019 3:20, Sowjanya Komatineni пишет:
> This patch adds support for saving OSC clock frequency and the
> drive-strength during OSC clock init and creates an API to restore
> OSC control register value from the saved context.
> 
> This API is invoked by Tegra210 clock driver during system resume
> to restore the  OSC clock settings.
> 
> Acked-by: Thierry Reding <treding@nvidia.com>
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> ---
>  drivers/clk/tegra/clk-tegra-fixed.c | 15 +++++++++++++++
>  drivers/clk/tegra/clk.h             |  1 +
>  2 files changed, 16 insertions(+)
> 
> diff --git a/drivers/clk/tegra/clk-tegra-fixed.c b/drivers/clk/tegra/clk-tegra-fixed.c
> index 8d91b2b191cf..7c6c8abfcde6 100644
> --- a/drivers/clk/tegra/clk-tegra-fixed.c
> +++ b/drivers/clk/tegra/clk-tegra-fixed.c
> @@ -17,6 +17,10 @@
>  #define OSC_CTRL			0x50
>  #define OSC_CTRL_OSC_FREQ_SHIFT		28
>  #define OSC_CTRL_PLL_REF_DIV_SHIFT	26
> +#define OSC_CTRL_MASK			(0x3f2 |	\
> +					(0xf << OSC_CTRL_OSC_FREQ_SHIFT))
> +
> +static u32 osc_ctrl_ctx;
>  
>  int __init tegra_osc_clk_init(void __iomem *clk_base, struct tegra_clk *clks,
>  			      unsigned long *input_freqs, unsigned int num,
> @@ -29,6 +33,7 @@ int __init tegra_osc_clk_init(void __iomem *clk_base, struct tegra_clk *clks,
>  	unsigned osc_idx;
>  
>  	val = readl_relaxed(clk_base + OSC_CTRL);
> +	osc_ctrl_ctx = val & OSC_CTRL_MASK;
>  	osc_idx = val >> OSC_CTRL_OSC_FREQ_SHIFT;
>  
>  	if (osc_idx < num)
> @@ -96,3 +101,13 @@ void __init tegra_fixed_clk_init(struct tegra_clk *tegra_clks)
>  		*dt_clk = clk;
>  	}
>  }
> +
> +void tegra_clk_osc_resume(void __iomem *clk_base)
> +{
> +	u32 val;
> +
> +	val = readl_relaxed(clk_base + OSC_CTRL) & ~OSC_CTRL_MASK;
> +	val |= osc_ctrl_ctx;
> +	writel_relaxed(val, clk_base + OSC_CTRL);

Why a full raw u32 OSC_CTRL value couldn't be simply saved and restored?

> +	fence_udelay(2, clk_base);
> +}
> diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
> index f1ef6ae8c979..abba6d8a04cd 100644
> --- a/drivers/clk/tegra/clk.h
> +++ b/drivers/clk/tegra/clk.h
> @@ -842,6 +842,7 @@ u16 tegra_pll_get_fixed_mdiv(struct clk_hw *hw, unsigned long input_rate);
>  int tegra_pll_p_div_to_hw(struct tegra_clk_pll *pll, u8 p_div);
>  int div_frac_get(unsigned long rate, unsigned parent_rate, u8 width,
>  		 u8 frac_width, u8 flags);
> +void tegra_clk_osc_resume(void __iomem *clk_base);
>  
>  
>  /* Combined read fence with delay */
> 

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

* Re: [PATCH v7 11/20] cpufreq: tegra124: Add suspend and resume support
  2019-07-31 10:23   ` Dmitry Osipenko
@ 2019-07-31 11:14     ` Dmitry Osipenko
  2019-07-31 21:05       ` Sowjanya Komatineni
  0 siblings, 1 reply; 67+ messages in thread
From: Dmitry Osipenko @ 2019-07-31 11:14 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree

31.07.2019 13:23, Dmitry Osipenko пишет:
> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>> This patch adds suspend and resume pm ops for cpufreq driver.
>>
>> PLLP is the safe clock source for CPU during system suspend and
>> resume as PLLP rate is below the CPU Fmax at Vmin.
>>
>> CPUFreq driver suspend switches the CPU clock source to PLLP and
>> disables the DFLL clock.
>>
>> During system resume, warmboot code powers up the CPU with PLLP
>> clock source. So CPUFreq driver resume enabled DFLL clock and
>> switches CPU back to DFLL clock source.
>>
>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>> ---
>>  drivers/cpufreq/tegra124-cpufreq.c | 60 ++++++++++++++++++++++++++++++++++++++
>>  1 file changed, 60 insertions(+)
>>
>> diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c
>> index 4f0c637b3b49..e979a3370988 100644
>> --- a/drivers/cpufreq/tegra124-cpufreq.c
>> +++ b/drivers/cpufreq/tegra124-cpufreq.c
>> @@ -6,6 +6,7 @@
>>  #define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
>>  
>>  #include <linux/clk.h>
>> +#include <linux/cpufreq.h>
>>  #include <linux/err.h>
>>  #include <linux/init.h>
>>  #include <linux/kernel.h>
>> @@ -128,8 +129,67 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
>>  	return ret;
>>  }
>>  
>> +static int __maybe_unused tegra124_cpufreq_suspend(struct device *dev)
>> +{
>> +	struct tegra124_cpufreq_priv *priv = dev_get_drvdata(dev);
>> +	int err;
>> +
>> +	/*
>> +	 * PLLP rate 408Mhz is below the CPU Fmax at Vmin and is safe to
>> +	 * use during suspend and resume. So, switch the CPU clock source
>> +	 * to PLLP and disable DFLL.
>> +	 */
>> +	err = clk_set_parent(priv->cpu_clk, priv->pllp_clk);
>> +	if (err < 0) {
>> +		dev_err(dev, "failed to reparent to PLLP: %d\n", err);
>> +		return err;
>> +	}
>> +
>> +	/* disable DFLL clock */
>> +	clk_disable_unprepare(priv->dfll_clk);
>> +
>> +	return 0;
>> +}
>> +
>> +static int __maybe_unused tegra124_cpufreq_resume(struct device *dev)
>> +{
>> +	struct tegra124_cpufreq_priv *priv = dev_get_drvdata(dev);
>> +	int err;
>> +
>> +	/*
>> +	 * Warmboot code powers up the CPU with PLLP clock source.
>> +	 * Enable DFLL clock and switch CPU clock source back to DFLL.
>> +	 */
>> +	err = clk_prepare_enable(priv->dfll_clk);
>> +	if (err < 0) {
>> +		dev_err(dev, "failed to enable DFLL clock for CPU: %d\n", err);
>> +		goto disable_cpufreq;
>> +	}
>> +
>> +	err = clk_set_parent(priv->cpu_clk, priv->dfll_clk);
>> +	if (err < 0) {
>> +		dev_err(dev, "failed to reparent to DFLL clock: %d\n", err);
>> +		goto disable_dfll;
>> +	}
>> +
>> +	return 0;
>> +
>> +disable_dfll:
>> +	clk_disable_unprepare(priv->dfll_clk);
>> +disable_cpufreq:
>> +	disable_cpufreq();
>> +
>> +	return err;
>> +}
>> +
>> +static const struct dev_pm_ops tegra124_cpufreq_pm_ops = {
>> +	SET_SYSTEM_SLEEP_PM_OPS(tegra124_cpufreq_suspend,
>> +				tegra124_cpufreq_resume)
>> +};
>> +
>>  static struct platform_driver tegra124_cpufreq_platdrv = {
>>  	.driver.name	= "cpufreq-tegra124",
>> +	.driver.pm	= &tegra124_cpufreq_pm_ops,
>>  	.probe		= tegra124_cpufreq_probe,
>>  };
>>  
>>
> 
> Looks good,
> 
> Reviewed-by: Dmitry Osipenko <digetx@gmail.com>
> 

BTW, you should also CC the CPUFreq maintainers because this patch can't
be applied without theirs ACK.

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

* Re: [PATCH v7 06/20] clk: tegra: Support for OSC context save and restore
  2019-07-31 11:11   ` Dmitry Osipenko
@ 2019-07-31 21:04     ` Sowjanya Komatineni
  2019-08-01 10:53       ` Dmitry Osipenko
  0 siblings, 1 reply; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-07-31 21:04 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree


On 7/31/19 4:11 AM, Dmitry Osipenko wrote:
> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>> This patch adds support for saving OSC clock frequency and the
>> drive-strength during OSC clock init and creates an API to restore
>> OSC control register value from the saved context.
>>
>> This API is invoked by Tegra210 clock driver during system resume
>> to restore the  OSC clock settings.
>>
>> Acked-by: Thierry Reding <treding@nvidia.com>
>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>> ---
>>   drivers/clk/tegra/clk-tegra-fixed.c | 15 +++++++++++++++
>>   drivers/clk/tegra/clk.h             |  1 +
>>   2 files changed, 16 insertions(+)
>>
>> diff --git a/drivers/clk/tegra/clk-tegra-fixed.c b/drivers/clk/tegra/clk-tegra-fixed.c
>> index 8d91b2b191cf..7c6c8abfcde6 100644
>> --- a/drivers/clk/tegra/clk-tegra-fixed.c
>> +++ b/drivers/clk/tegra/clk-tegra-fixed.c
>> @@ -17,6 +17,10 @@
>>   #define OSC_CTRL			0x50
>>   #define OSC_CTRL_OSC_FREQ_SHIFT		28
>>   #define OSC_CTRL_PLL_REF_DIV_SHIFT	26
>> +#define OSC_CTRL_MASK			(0x3f2 |	\
>> +					(0xf << OSC_CTRL_OSC_FREQ_SHIFT))
>> +
>> +static u32 osc_ctrl_ctx;
>>   
>>   int __init tegra_osc_clk_init(void __iomem *clk_base, struct tegra_clk *clks,
>>   			      unsigned long *input_freqs, unsigned int num,
>> @@ -29,6 +33,7 @@ int __init tegra_osc_clk_init(void __iomem *clk_base, struct tegra_clk *clks,
>>   	unsigned osc_idx;
>>   
>>   	val = readl_relaxed(clk_base + OSC_CTRL);
>> +	osc_ctrl_ctx = val & OSC_CTRL_MASK;
>>   	osc_idx = val >> OSC_CTRL_OSC_FREQ_SHIFT;
>>   
>>   	if (osc_idx < num)
>> @@ -96,3 +101,13 @@ void __init tegra_fixed_clk_init(struct tegra_clk *tegra_clks)
>>   		*dt_clk = clk;
>>   	}
>>   }
>> +
>> +void tegra_clk_osc_resume(void __iomem *clk_base)
>> +{
>> +	u32 val;
>> +
>> +	val = readl_relaxed(clk_base + OSC_CTRL) & ~OSC_CTRL_MASK;
>> +	val |= osc_ctrl_ctx;
>> +	writel_relaxed(val, clk_base + OSC_CTRL);
> Why a full raw u32 OSC_CTRL value couldn't be simply saved and restored?

Storing and restoring only required fields to avoid accidental 
misconfiguration.

OSC_CTRL register has other bits (PLL_REF_DIV) which are configured by 
BR depending on OSC_FREQ and also setting PLL_REF_DIV while PLLS are in 
use is not safe.

>> +	fence_udelay(2, clk_base);
>> +}
>> diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
>> index f1ef6ae8c979..abba6d8a04cd 100644
>> --- a/drivers/clk/tegra/clk.h
>> +++ b/drivers/clk/tegra/clk.h
>> @@ -842,6 +842,7 @@ u16 tegra_pll_get_fixed_mdiv(struct clk_hw *hw, unsigned long input_rate);
>>   int tegra_pll_p_div_to_hw(struct tegra_clk_pll *pll, u8 p_div);
>>   int div_frac_get(unsigned long rate, unsigned parent_rate, u8 width,
>>   		 u8 frac_width, u8 flags);
>> +void tegra_clk_osc_resume(void __iomem *clk_base);
>>   
>>   
>>   /* Combined read fence with delay */
>>

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

* Re: [PATCH v7 11/20] cpufreq: tegra124: Add suspend and resume support
  2019-07-31 11:14     ` Dmitry Osipenko
@ 2019-07-31 21:05       ` Sowjanya Komatineni
  0 siblings, 0 replies; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-07-31 21:05 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree


On 7/31/19 4:14 AM, Dmitry Osipenko wrote:
> 31.07.2019 13:23, Dmitry Osipenko пишет:
>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>> This patch adds suspend and resume pm ops for cpufreq driver.
>>>
>>> PLLP is the safe clock source for CPU during system suspend and
>>> resume as PLLP rate is below the CPU Fmax at Vmin.
>>>
>>> CPUFreq driver suspend switches the CPU clock source to PLLP and
>>> disables the DFLL clock.
>>>
>>> During system resume, warmboot code powers up the CPU with PLLP
>>> clock source. So CPUFreq driver resume enabled DFLL clock and
>>> switches CPU back to DFLL clock source.
>>>
>>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>>> ---
>>>   drivers/cpufreq/tegra124-cpufreq.c | 60 ++++++++++++++++++++++++++++++++++++++
>>>   1 file changed, 60 insertions(+)
>>>
>>> diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c
>>> index 4f0c637b3b49..e979a3370988 100644
>>> --- a/drivers/cpufreq/tegra124-cpufreq.c
>>> +++ b/drivers/cpufreq/tegra124-cpufreq.c
>>> @@ -6,6 +6,7 @@
>>>   #define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
>>>   
>>>   #include <linux/clk.h>
>>> +#include <linux/cpufreq.h>
>>>   #include <linux/err.h>
>>>   #include <linux/init.h>
>>>   #include <linux/kernel.h>
>>> @@ -128,8 +129,67 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
>>>   	return ret;
>>>   }
>>>   
>>> +static int __maybe_unused tegra124_cpufreq_suspend(struct device *dev)
>>> +{
>>> +	struct tegra124_cpufreq_priv *priv = dev_get_drvdata(dev);
>>> +	int err;
>>> +
>>> +	/*
>>> +	 * PLLP rate 408Mhz is below the CPU Fmax at Vmin and is safe to
>>> +	 * use during suspend and resume. So, switch the CPU clock source
>>> +	 * to PLLP and disable DFLL.
>>> +	 */
>>> +	err = clk_set_parent(priv->cpu_clk, priv->pllp_clk);
>>> +	if (err < 0) {
>>> +		dev_err(dev, "failed to reparent to PLLP: %d\n", err);
>>> +		return err;
>>> +	}
>>> +
>>> +	/* disable DFLL clock */
>>> +	clk_disable_unprepare(priv->dfll_clk);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static int __maybe_unused tegra124_cpufreq_resume(struct device *dev)
>>> +{
>>> +	struct tegra124_cpufreq_priv *priv = dev_get_drvdata(dev);
>>> +	int err;
>>> +
>>> +	/*
>>> +	 * Warmboot code powers up the CPU with PLLP clock source.
>>> +	 * Enable DFLL clock and switch CPU clock source back to DFLL.
>>> +	 */
>>> +	err = clk_prepare_enable(priv->dfll_clk);
>>> +	if (err < 0) {
>>> +		dev_err(dev, "failed to enable DFLL clock for CPU: %d\n", err);
>>> +		goto disable_cpufreq;
>>> +	}
>>> +
>>> +	err = clk_set_parent(priv->cpu_clk, priv->dfll_clk);
>>> +	if (err < 0) {
>>> +		dev_err(dev, "failed to reparent to DFLL clock: %d\n", err);
>>> +		goto disable_dfll;
>>> +	}
>>> +
>>> +	return 0;
>>> +
>>> +disable_dfll:
>>> +	clk_disable_unprepare(priv->dfll_clk);
>>> +disable_cpufreq:
>>> +	disable_cpufreq();
>>> +
>>> +	return err;
>>> +}
>>> +
>>> +static const struct dev_pm_ops tegra124_cpufreq_pm_ops = {
>>> +	SET_SYSTEM_SLEEP_PM_OPS(tegra124_cpufreq_suspend,
>>> +				tegra124_cpufreq_resume)
>>> +};
>>> +
>>>   static struct platform_driver tegra124_cpufreq_platdrv = {
>>>   	.driver.name	= "cpufreq-tegra124",
>>> +	.driver.pm	= &tegra124_cpufreq_pm_ops,
>>>   	.probe		= tegra124_cpufreq_probe,
>>>   };
>>>   
>>>
>> Looks good,
>>
>> Reviewed-by: Dmitry Osipenko <digetx@gmail.com>
>>
> BTW, you should also CC the CPUFreq maintainers because this patch can't
> be applied without theirs ACK.
MIssed them. Will resend v7 series with CPUFreq maintainers...

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

* Re: [PATCH v7 16/20] arm64: tegra: Enable wake from deep sleep on RTC alarm
  2019-07-31 11:04   ` Dmitry Osipenko
@ 2019-07-31 21:08     ` Sowjanya Komatineni
  2019-08-01 10:43       ` Dmitry Osipenko
  0 siblings, 1 reply; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-07-31 21:08 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree


On 7/31/19 4:04 AM, Dmitry Osipenko wrote:
> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>> This patch updates device tree for RTC and PMC to allow system wake
>> from deep sleep on RTC alarm.
>>
>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>> ---
>>   arch/arm64/boot/dts/nvidia/tegra210.dtsi | 5 ++++-
>>   1 file changed, 4 insertions(+), 1 deletion(-)
>>
>> diff --git a/arch/arm64/boot/dts/nvidia/tegra210.dtsi b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
>> index 659753118e96..30a7c48385a2 100644
>> --- a/arch/arm64/boot/dts/nvidia/tegra210.dtsi
>> +++ b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
>> @@ -768,7 +768,8 @@
>>   	rtc@7000e000 {
>>   		compatible = "nvidia,tegra210-rtc", "nvidia,tegra20-rtc";
>>   		reg = <0x0 0x7000e000 0x0 0x100>;
>> -		interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
>> +		interrupts = <16 IRQ_TYPE_LEVEL_HIGH>;
>> +		interrupt-parent = <&pmc>;
>>   		clocks = <&tegra_car TEGRA210_CLK_RTC>;
>>   		clock-names = "rtc";
>>   	};
>> @@ -778,6 +779,8 @@
>>   		reg = <0x0 0x7000e400 0x0 0x400>;
>>   		clocks = <&tegra_car TEGRA210_CLK_PCLK>, <&clk32k_in>;
>>   		clock-names = "pclk", "clk32k_in";
>> +		#interrupt-cells = <2>;
>> +		interrupt-controller;
>>   
>>   		powergates {
>>   			pd_audio: aud {
>>
> Is this a backwards-compatible change? Or it's not really worth to care
> about the compatibility with older kernel versions, I'm not sure about
> overall state of T210 in the upstream kernel.
I don't think its required to be backwards-compatible as SC7 entry/exit 
implementation for T210 is with this patch series onwards..

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-07-31 10:44     ` Dmitry Osipenko
@ 2019-07-31 23:09       ` Sowjanya Komatineni
  2019-08-01 17:58         ` Sowjanya Komatineni
  0 siblings, 1 reply; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-07-31 23:09 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree


On 7/31/19 3:44 AM, Dmitry Osipenko wrote:
> 31.07.2019 12:50, Dmitry Osipenko пишет:
>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>> This patch implements save and restore context for peripheral fixed
>>> clock ops, peripheral gate clock ops, sdmmc mux clock ops, and
>>> peripheral clock ops.
>>>
>>> During system suspend, core power goes off and looses the settings
>>> of the Tegra CAR controller registers.
>>>
>>> So during suspend entry clock and reset state of peripherals is saved
>>> and on resume they are restored to have clocks back to same rate and
>>> state as before suspend.
>>>
>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>>> ---
>>>   drivers/clk/tegra/clk-periph-fixed.c | 33 ++++++++++++++++++++++++++++++++
>>>   drivers/clk/tegra/clk-periph-gate.c  | 34 +++++++++++++++++++++++++++++++++
>>>   drivers/clk/tegra/clk-periph.c       | 37 ++++++++++++++++++++++++++++++++++++
>>>   drivers/clk/tegra/clk-sdmmc-mux.c    | 28 +++++++++++++++++++++++++++
>>>   drivers/clk/tegra/clk.h              |  6 ++++++
>>>   5 files changed, 138 insertions(+)
>>>
>>> diff --git a/drivers/clk/tegra/clk-periph-fixed.c b/drivers/clk/tegra/clk-periph-fixed.c
>>> index c088e7a280df..21b24530fa00 100644
>>> --- a/drivers/clk/tegra/clk-periph-fixed.c
>>> +++ b/drivers/clk/tegra/clk-periph-fixed.c
>>> @@ -60,11 +60,44 @@ tegra_clk_periph_fixed_recalc_rate(struct clk_hw *hw,
>>>   	return (unsigned long)rate;
>>>   }
>>>   
>>> +static int tegra_clk_periph_fixed_save_context(struct clk_hw *hw)
>>> +{
>>> +	struct tegra_clk_periph_fixed *fixed = to_tegra_clk_periph_fixed(hw);
>>> +	u32 mask = 1 << (fixed->num % 32);
>>> +
>>> +	fixed->enb_ctx = readl_relaxed(fixed->base + fixed->regs->enb_reg) &
>>> +			 mask;
>>> +	fixed->rst_ctx = readl_relaxed(fixed->base + fixed->regs->rst_reg) &
>>> +			 mask;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static void tegra_clk_periph_fixed_restore_context(struct clk_hw *hw)
>>> +{
>>> +	struct tegra_clk_periph_fixed *fixed = to_tegra_clk_periph_fixed(hw);
>>> +	u32 mask = 1 << (fixed->num % 32);
>>> +
>>> +	if (fixed->enb_ctx)
>>> +		writel_relaxed(mask, fixed->base + fixed->regs->enb_set_reg);
>>> +	else
>>> +		writel_relaxed(mask, fixed->base + fixed->regs->enb_clr_reg);
>>> +
>>> +	udelay(2);
>>> +
>>> +	if (!fixed->rst_ctx) {
>>> +		udelay(5); /* reset propogation delay */
>>> +		writel_relaxed(mask, fixed->base + fixed->regs->rst_reg);
>>> +	}
>>> +}
>>> +
>>>   static const struct clk_ops tegra_clk_periph_fixed_ops = {
>>>   	.is_enabled = tegra_clk_periph_fixed_is_enabled,
>>>   	.enable = tegra_clk_periph_fixed_enable,
>>>   	.disable = tegra_clk_periph_fixed_disable,
>>>   	.recalc_rate = tegra_clk_periph_fixed_recalc_rate,
>>> +	.save_context = tegra_clk_periph_fixed_save_context,
>>> +	.restore_context = tegra_clk_periph_fixed_restore_context,
>>>   };
>>>   
>>>   struct clk *tegra_clk_register_periph_fixed(const char *name,
>>> diff --git a/drivers/clk/tegra/clk-periph-gate.c b/drivers/clk/tegra/clk-periph-gate.c
>>> index 4b31beefc9fc..6ba5b08e0787 100644
>>> --- a/drivers/clk/tegra/clk-periph-gate.c
>>> +++ b/drivers/clk/tegra/clk-periph-gate.c
>>> @@ -25,6 +25,8 @@ static DEFINE_SPINLOCK(periph_ref_lock);
>>>   
>>>   #define read_rst(gate) \
>>>   	readl_relaxed(gate->clk_base + (gate->regs->rst_reg))
>>> +#define write_rst_set(val, gate) \
>>> +	writel_relaxed(val, gate->clk_base + (gate->regs->rst_set_reg))
>>>   #define write_rst_clr(val, gate) \
>>>   	writel_relaxed(val, gate->clk_base + (gate->regs->rst_clr_reg))
>>>   
>>> @@ -110,10 +112,42 @@ static void clk_periph_disable(struct clk_hw *hw)
>>>   	spin_unlock_irqrestore(&periph_ref_lock, flags);
>>>   }
>>>   
>>> +static int clk_periph_gate_save_context(struct clk_hw *hw)
>>> +{
>>> +	struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
>>> +
>>> +	gate->clk_state_ctx = read_enb(gate) & periph_clk_to_bit(gate);
>>> +	gate->rst_state_ctx = read_rst(gate) & periph_clk_to_bit(gate);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static void clk_periph_gate_restore_context(struct clk_hw *hw)
>>> +{
>>> +	struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
>>> +
>>> +	if (gate->clk_state_ctx)
>>> +		write_enb_set(periph_clk_to_bit(gate), gate);
>>> +	else
>>> +		write_enb_clr(periph_clk_to_bit(gate), gate);
>>> +
>>> +	udelay(5);
>>> +
>>> +	if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
>>> +	    !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
>>> +		if (gate->rst_state_ctx)
>>> +			write_rst_set(periph_clk_to_bit(gate), gate);
>>> +		else
>>> +			write_rst_clr(periph_clk_to_bit(gate), gate);
>>> +	}
>>> +}
>>> +
>>>   const struct clk_ops tegra_clk_periph_gate_ops = {
>>>   	.is_enabled = clk_periph_is_enabled,
>>>   	.enable = clk_periph_enable,
>>>   	.disable = clk_periph_disable,
>>> +	.save_context = clk_periph_gate_save_context,
>>> +	.restore_context = clk_periph_gate_restore_context,
>>>   };
>>>   
>>>   struct clk *tegra_clk_register_periph_gate(const char *name,
>>> diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c
>>> index 58437da25156..06fb62955768 100644
>>> --- a/drivers/clk/tegra/clk-periph.c
>>> +++ b/drivers/clk/tegra/clk-periph.c
>>> @@ -99,6 +99,37 @@ static void clk_periph_disable(struct clk_hw *hw)
>>>   	gate_ops->disable(gate_hw);
>>>   }
>>>   
>>> +static int clk_periph_save_context(struct clk_hw *hw)
>>> +{
>>> +	struct tegra_clk_periph *periph = to_clk_periph(hw);
>>> +	const struct clk_ops *gate_ops = periph->gate_ops;
>>> +	struct clk_hw *gate_hw = &periph->gate.hw;
>>> +
>>> +	if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
>>> +		gate_ops->save_context(gate_hw);
>>> +
>>> +	periph->parent_ctx = clk_periph_get_parent(hw);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static void clk_periph_restore_context(struct clk_hw *hw)
>>> +{
>>> +	struct tegra_clk_periph *periph = to_clk_periph(hw);
>>> +	const struct clk_ops *gate_ops = periph->gate_ops;
>>> +	struct clk_hw *gate_hw = &periph->gate.hw;
>>> +	const struct clk_ops *div_ops = periph->div_ops;
>>> +	struct clk_hw *div_hw = &periph->divider.hw;
>>> +
>>> +	clk_periph_set_parent(hw, periph->parent_ctx);
>>> +
>>> +	if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
>>> +		div_ops->restore_context(div_hw);
>> Could you please point to where the divider's save_context() happens?
>> Because I can't see it.
> Ah, I now see that there is no need to save the dividers context because
> clk itself has enough info that is needed for the context's restoring
> (like I pointed in the review to v6).
>
> Looks like you could also implement a new clk_hw_get_parent_index()
> generic helper to get the index instead of storing it manually.

clk_periph_get_parent basically invokes existing clk_mux_ops 
get_parent() which is then saved in tegra_clk_periph.

All existing drivers are using directly get_parent() from clk_mux which 
actually gets index from the register read.

To have this more generic w.r.t save/restore context point of view, 
probably instead of implementing new get_parent_index helper, I think 
its better to implement save_context and restore_context to clk_mux_ops 
along with creating parent_index field into clk_mux to cache index 
during set_parent.

So we just need to invoke mux_ops save_context and restore_context.

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

* Re: [PATCH v7 16/20] arm64: tegra: Enable wake from deep sleep on RTC alarm
  2019-07-31 21:08     ` Sowjanya Komatineni
@ 2019-08-01 10:43       ` Dmitry Osipenko
  2019-08-01 17:56         ` Sowjanya Komatineni
  0 siblings, 1 reply; 67+ messages in thread
From: Dmitry Osipenko @ 2019-08-01 10:43 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree

01.08.2019 0:08, Sowjanya Komatineni пишет:
> 
> On 7/31/19 4:04 AM, Dmitry Osipenko wrote:
>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>> This patch updates device tree for RTC and PMC to allow system wake
>>> from deep sleep on RTC alarm.
>>>
>>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>>> ---
>>>   arch/arm64/boot/dts/nvidia/tegra210.dtsi | 5 ++++-
>>>   1 file changed, 4 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/arch/arm64/boot/dts/nvidia/tegra210.dtsi
>>> b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
>>> index 659753118e96..30a7c48385a2 100644
>>> --- a/arch/arm64/boot/dts/nvidia/tegra210.dtsi
>>> +++ b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
>>> @@ -768,7 +768,8 @@
>>>       rtc@7000e000 {
>>>           compatible = "nvidia,tegra210-rtc", "nvidia,tegra20-rtc";
>>>           reg = <0x0 0x7000e000 0x0 0x100>;
>>> -        interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
>>> +        interrupts = <16 IRQ_TYPE_LEVEL_HIGH>;
>>> +        interrupt-parent = <&pmc>;
>>>           clocks = <&tegra_car TEGRA210_CLK_RTC>;
>>>           clock-names = "rtc";
>>>       };
>>> @@ -778,6 +779,8 @@
>>>           reg = <0x0 0x7000e400 0x0 0x400>;
>>>           clocks = <&tegra_car TEGRA210_CLK_PCLK>, <&clk32k_in>;
>>>           clock-names = "pclk", "clk32k_in";
>>> +        #interrupt-cells = <2>;
>>> +        interrupt-controller;
>>>             powergates {
>>>               pd_audio: aud {
>>>
>> Is this a backwards-compatible change? Or it's not really worth to care
>> about the compatibility with older kernel versions, I'm not sure about
>> overall state of T210 in the upstream kernel.
> I don't think its required to be backwards-compatible as SC7 entry/exit
> implementation for T210 is with this patch series onwards..

The new device tree binary should work with older kernel versions, AFAIK
this is the upstream rule. But if kernel support isn't in a very good
shape and not much people are using it, then obviously it is not very
important.

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

* Re: [PATCH v7 06/20] clk: tegra: Support for OSC context save and restore
  2019-07-31 21:04     ` Sowjanya Komatineni
@ 2019-08-01 10:53       ` Dmitry Osipenko
  2019-08-01 18:06         ` Sowjanya Komatineni
  0 siblings, 1 reply; 67+ messages in thread
From: Dmitry Osipenko @ 2019-08-01 10:53 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree

01.08.2019 0:04, Sowjanya Komatineni пишет:
> 
> On 7/31/19 4:11 AM, Dmitry Osipenko wrote:
>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>> This patch adds support for saving OSC clock frequency and the
>>> drive-strength during OSC clock init and creates an API to restore
>>> OSC control register value from the saved context.
>>>
>>> This API is invoked by Tegra210 clock driver during system resume
>>> to restore the  OSC clock settings.
>>>
>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>>> ---
>>>   drivers/clk/tegra/clk-tegra-fixed.c | 15 +++++++++++++++
>>>   drivers/clk/tegra/clk.h             |  1 +
>>>   2 files changed, 16 insertions(+)
>>>
>>> diff --git a/drivers/clk/tegra/clk-tegra-fixed.c
>>> b/drivers/clk/tegra/clk-tegra-fixed.c
>>> index 8d91b2b191cf..7c6c8abfcde6 100644
>>> --- a/drivers/clk/tegra/clk-tegra-fixed.c
>>> +++ b/drivers/clk/tegra/clk-tegra-fixed.c
>>> @@ -17,6 +17,10 @@
>>>   #define OSC_CTRL            0x50
>>>   #define OSC_CTRL_OSC_FREQ_SHIFT        28
>>>   #define OSC_CTRL_PLL_REF_DIV_SHIFT    26
>>> +#define OSC_CTRL_MASK            (0x3f2 |    \
>>> +                    (0xf << OSC_CTRL_OSC_FREQ_SHIFT))
>>> +
>>> +static u32 osc_ctrl_ctx;
>>>     int __init tegra_osc_clk_init(void __iomem *clk_base, struct
>>> tegra_clk *clks,
>>>                     unsigned long *input_freqs, unsigned int num,
>>> @@ -29,6 +33,7 @@ int __init tegra_osc_clk_init(void __iomem
>>> *clk_base, struct tegra_clk *clks,
>>>       unsigned osc_idx;
>>>         val = readl_relaxed(clk_base + OSC_CTRL);
>>> +    osc_ctrl_ctx = val & OSC_CTRL_MASK;
>>>       osc_idx = val >> OSC_CTRL_OSC_FREQ_SHIFT;
>>>         if (osc_idx < num)
>>> @@ -96,3 +101,13 @@ void __init tegra_fixed_clk_init(struct tegra_clk
>>> *tegra_clks)
>>>           *dt_clk = clk;
>>>       }
>>>   }
>>> +
>>> +void tegra_clk_osc_resume(void __iomem *clk_base)
>>> +{
>>> +    u32 val;
>>> +
>>> +    val = readl_relaxed(clk_base + OSC_CTRL) & ~OSC_CTRL_MASK;
>>> +    val |= osc_ctrl_ctx;
>>> +    writel_relaxed(val, clk_base + OSC_CTRL);
>> Why a full raw u32 OSC_CTRL value couldn't be simply saved and restored?
> 
> Storing and restoring only required fields to avoid accidental
> misconfiguration.
> 
> OSC_CTRL register has other bits (PLL_REF_DIV) which are configured by
> BR depending on OSC_FREQ and also setting PLL_REF_DIV while PLLS are in
> use is not safe.

I'm looking at the clk-driver sources and see that none of the Tegra
drivers ever change the OSC_CTRL configuration, T30/114 even have
#defines for the OSC_CTRL that are unused.

So, this leads to a question.. does any bootloader really ever change
the OSC_CTRL such that it differs after resume from suspend in
comparison to the value at the time of kernel's booting up?

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

* Re: [PATCH v7 16/20] arm64: tegra: Enable wake from deep sleep on RTC alarm
  2019-08-01 10:43       ` Dmitry Osipenko
@ 2019-08-01 17:56         ` Sowjanya Komatineni
  2019-08-01 18:39           ` Dmitry Osipenko
  0 siblings, 1 reply; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-08-01 17:56 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree


On 8/1/19 3:43 AM, Dmitry Osipenko wrote:
> 01.08.2019 0:08, Sowjanya Komatineni пишет:
>> On 7/31/19 4:04 AM, Dmitry Osipenko wrote:
>>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>>> This patch updates device tree for RTC and PMC to allow system wake
>>>> from deep sleep on RTC alarm.
>>>>
>>>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>>>> ---
>>>>    arch/arm64/boot/dts/nvidia/tegra210.dtsi | 5 ++++-
>>>>    1 file changed, 4 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git a/arch/arm64/boot/dts/nvidia/tegra210.dtsi
>>>> b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
>>>> index 659753118e96..30a7c48385a2 100644
>>>> --- a/arch/arm64/boot/dts/nvidia/tegra210.dtsi
>>>> +++ b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
>>>> @@ -768,7 +768,8 @@
>>>>        rtc@7000e000 {
>>>>            compatible = "nvidia,tegra210-rtc", "nvidia,tegra20-rtc";
>>>>            reg = <0x0 0x7000e000 0x0 0x100>;
>>>> -        interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
>>>> +        interrupts = <16 IRQ_TYPE_LEVEL_HIGH>;
>>>> +        interrupt-parent = <&pmc>;
>>>>            clocks = <&tegra_car TEGRA210_CLK_RTC>;
>>>>            clock-names = "rtc";
>>>>        };
>>>> @@ -778,6 +779,8 @@
>>>>            reg = <0x0 0x7000e400 0x0 0x400>;
>>>>            clocks = <&tegra_car TEGRA210_CLK_PCLK>, <&clk32k_in>;
>>>>            clock-names = "pclk", "clk32k_in";
>>>> +        #interrupt-cells = <2>;
>>>> +        interrupt-controller;
>>>>              powergates {
>>>>                pd_audio: aud {
>>>>
>>> Is this a backwards-compatible change? Or it's not really worth to care
>>> about the compatibility with older kernel versions, I'm not sure about
>>> overall state of T210 in the upstream kernel.
>> I don't think its required to be backwards-compatible as SC7 entry/exit
>> implementation for T210 is with this patch series onwards..
> The new device tree binary should work with older kernel versions, AFAIK
> this is the upstream rule. But if kernel support isn't in a very good
> shape and not much people are using it, then obviously it is not very
> important.

Yes, my response to backwards-compatible was with respect to interrupt 
parent change as this will not be backward compatible and also there is 
no Tegra210 suspend/resume earlier. Other functionality wise, it is 
backward compatible.

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-07-31 23:09       ` Sowjanya Komatineni
@ 2019-08-01 17:58         ` Sowjanya Komatineni
  2019-08-01 19:00           ` Dmitry Osipenko
  0 siblings, 1 reply; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-08-01 17:58 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree


On 7/31/19 4:09 PM, Sowjanya Komatineni wrote:
>
> On 7/31/19 3:44 AM, Dmitry Osipenko wrote:
>> 31.07.2019 12:50, Dmitry Osipenko пишет:
>>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>>> This patch implements save and restore context for peripheral fixed
>>>> clock ops, peripheral gate clock ops, sdmmc mux clock ops, and
>>>> peripheral clock ops.
>>>>
>>>> During system suspend, core power goes off and looses the settings
>>>> of the Tegra CAR controller registers.
>>>>
>>>> So during suspend entry clock and reset state of peripherals is saved
>>>> and on resume they are restored to have clocks back to same rate and
>>>> state as before suspend.
>>>>
>>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>>>> ---
>>>>   drivers/clk/tegra/clk-periph-fixed.c | 33 
>>>> ++++++++++++++++++++++++++++++++
>>>>   drivers/clk/tegra/clk-periph-gate.c  | 34 
>>>> +++++++++++++++++++++++++++++++++
>>>>   drivers/clk/tegra/clk-periph.c       | 37 
>>>> ++++++++++++++++++++++++++++++++++++
>>>>   drivers/clk/tegra/clk-sdmmc-mux.c    | 28 
>>>> +++++++++++++++++++++++++++
>>>>   drivers/clk/tegra/clk.h              |  6 ++++++
>>>>   5 files changed, 138 insertions(+)
>>>>
>>>> diff --git a/drivers/clk/tegra/clk-periph-fixed.c 
>>>> b/drivers/clk/tegra/clk-periph-fixed.c
>>>> index c088e7a280df..21b24530fa00 100644
>>>> --- a/drivers/clk/tegra/clk-periph-fixed.c
>>>> +++ b/drivers/clk/tegra/clk-periph-fixed.c
>>>> @@ -60,11 +60,44 @@ tegra_clk_periph_fixed_recalc_rate(struct 
>>>> clk_hw *hw,
>>>>       return (unsigned long)rate;
>>>>   }
>>>>   +static int tegra_clk_periph_fixed_save_context(struct clk_hw *hw)
>>>> +{
>>>> +    struct tegra_clk_periph_fixed *fixed = 
>>>> to_tegra_clk_periph_fixed(hw);
>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>> +
>>>> +    fixed->enb_ctx = readl_relaxed(fixed->base + 
>>>> fixed->regs->enb_reg) &
>>>> +             mask;
>>>> +    fixed->rst_ctx = readl_relaxed(fixed->base + 
>>>> fixed->regs->rst_reg) &
>>>> +             mask;
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static void tegra_clk_periph_fixed_restore_context(struct clk_hw *hw)
>>>> +{
>>>> +    struct tegra_clk_periph_fixed *fixed = 
>>>> to_tegra_clk_periph_fixed(hw);
>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>> +
>>>> +    if (fixed->enb_ctx)
>>>> +        writel_relaxed(mask, fixed->base + fixed->regs->enb_set_reg);
>>>> +    else
>>>> +        writel_relaxed(mask, fixed->base + fixed->regs->enb_clr_reg);
>>>> +
>>>> +    udelay(2);
>>>> +
>>>> +    if (!fixed->rst_ctx) {
>>>> +        udelay(5); /* reset propogation delay */
>>>> +        writel_relaxed(mask, fixed->base + fixed->regs->rst_reg);
>>>> +    }
>>>> +}
>>>> +
>>>>   static const struct clk_ops tegra_clk_periph_fixed_ops = {
>>>>       .is_enabled = tegra_clk_periph_fixed_is_enabled,
>>>>       .enable = tegra_clk_periph_fixed_enable,
>>>>       .disable = tegra_clk_periph_fixed_disable,
>>>>       .recalc_rate = tegra_clk_periph_fixed_recalc_rate,
>>>> +    .save_context = tegra_clk_periph_fixed_save_context,
>>>> +    .restore_context = tegra_clk_periph_fixed_restore_context,
>>>>   };
>>>>     struct clk *tegra_clk_register_periph_fixed(const char *name,
>>>> diff --git a/drivers/clk/tegra/clk-periph-gate.c 
>>>> b/drivers/clk/tegra/clk-periph-gate.c
>>>> index 4b31beefc9fc..6ba5b08e0787 100644
>>>> --- a/drivers/clk/tegra/clk-periph-gate.c
>>>> +++ b/drivers/clk/tegra/clk-periph-gate.c
>>>> @@ -25,6 +25,8 @@ static DEFINE_SPINLOCK(periph_ref_lock);
>>>>     #define read_rst(gate) \
>>>>       readl_relaxed(gate->clk_base + (gate->regs->rst_reg))
>>>> +#define write_rst_set(val, gate) \
>>>> +    writel_relaxed(val, gate->clk_base + (gate->regs->rst_set_reg))
>>>>   #define write_rst_clr(val, gate) \
>>>>       writel_relaxed(val, gate->clk_base + (gate->regs->rst_clr_reg))
>>>>   @@ -110,10 +112,42 @@ static void clk_periph_disable(struct 
>>>> clk_hw *hw)
>>>>       spin_unlock_irqrestore(&periph_ref_lock, flags);
>>>>   }
>>>>   +static int clk_periph_gate_save_context(struct clk_hw *hw)
>>>> +{
>>>> +    struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
>>>> +
>>>> +    gate->clk_state_ctx = read_enb(gate) & periph_clk_to_bit(gate);
>>>> +    gate->rst_state_ctx = read_rst(gate) & periph_clk_to_bit(gate);
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static void clk_periph_gate_restore_context(struct clk_hw *hw)
>>>> +{
>>>> +    struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
>>>> +
>>>> +    if (gate->clk_state_ctx)
>>>> +        write_enb_set(periph_clk_to_bit(gate), gate);
>>>> +    else
>>>> +        write_enb_clr(periph_clk_to_bit(gate), gate);
>>>> +
>>>> +    udelay(5);
>>>> +
>>>> +    if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
>>>> +        !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
>>>> +        if (gate->rst_state_ctx)
>>>> +            write_rst_set(periph_clk_to_bit(gate), gate);
>>>> +        else
>>>> +            write_rst_clr(periph_clk_to_bit(gate), gate);
>>>> +    }
>>>> +}
>>>> +
>>>>   const struct clk_ops tegra_clk_periph_gate_ops = {
>>>>       .is_enabled = clk_periph_is_enabled,
>>>>       .enable = clk_periph_enable,
>>>>       .disable = clk_periph_disable,
>>>> +    .save_context = clk_periph_gate_save_context,
>>>> +    .restore_context = clk_periph_gate_restore_context,
>>>>   };
>>>>     struct clk *tegra_clk_register_periph_gate(const char *name,
>>>> diff --git a/drivers/clk/tegra/clk-periph.c 
>>>> b/drivers/clk/tegra/clk-periph.c
>>>> index 58437da25156..06fb62955768 100644
>>>> --- a/drivers/clk/tegra/clk-periph.c
>>>> +++ b/drivers/clk/tegra/clk-periph.c
>>>> @@ -99,6 +99,37 @@ static void clk_periph_disable(struct clk_hw *hw)
>>>>       gate_ops->disable(gate_hw);
>>>>   }
>>>>   +static int clk_periph_save_context(struct clk_hw *hw)
>>>> +{
>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>> +
>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
>>>> +        gate_ops->save_context(gate_hw);
>>>> +
>>>> +    periph->parent_ctx = clk_periph_get_parent(hw);
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static void clk_periph_restore_context(struct clk_hw *hw)
>>>> +{
>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>> +    const struct clk_ops *div_ops = periph->div_ops;
>>>> +    struct clk_hw *div_hw = &periph->divider.hw;
>>>> +
>>>> +    clk_periph_set_parent(hw, periph->parent_ctx);
>>>> +
>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
>>>> +        div_ops->restore_context(div_hw);
>>> Could you please point to where the divider's save_context() happens?
>>> Because I can't see it.
>> Ah, I now see that there is no need to save the dividers context because
>> clk itself has enough info that is needed for the context's restoring
>> (like I pointed in the review to v6).
>>
>> Looks like you could also implement a new clk_hw_get_parent_index()
>> generic helper to get the index instead of storing it manually.
>
> clk_periph_get_parent basically invokes existing clk_mux_ops 
> get_parent() which is then saved in tegra_clk_periph.
>
> All existing drivers are using directly get_parent() from clk_mux 
> which actually gets index from the register read.
>
> To have this more generic w.r.t save/restore context point of view, 
> probably instead of implementing new get_parent_index helper, I think 
> its better to implement save_context and restore_context to 
> clk_mux_ops along with creating parent_index field into clk_mux to 
> cache index during set_parent.
>
> So we just need to invoke mux_ops save_context and restore_context.
>
I hope its ok to add save/restore context to clk_mux_ops to be more 
generic w.r.t save/restore context rather than get_parent_index API. 
Please confirm if you agree.

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

* Re: [PATCH v7 06/20] clk: tegra: Support for OSC context save and restore
  2019-08-01 10:53       ` Dmitry Osipenko
@ 2019-08-01 18:06         ` Sowjanya Komatineni
  2019-08-01 18:42           ` Dmitry Osipenko
  0 siblings, 1 reply; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-08-01 18:06 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree


On 8/1/19 3:53 AM, Dmitry Osipenko wrote:
> 01.08.2019 0:04, Sowjanya Komatineni пишет:
>> On 7/31/19 4:11 AM, Dmitry Osipenko wrote:
>>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>>> This patch adds support for saving OSC clock frequency and the
>>>> drive-strength during OSC clock init and creates an API to restore
>>>> OSC control register value from the saved context.
>>>>
>>>> This API is invoked by Tegra210 clock driver during system resume
>>>> to restore the  OSC clock settings.
>>>>
>>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>>>> ---
>>>>    drivers/clk/tegra/clk-tegra-fixed.c | 15 +++++++++++++++
>>>>    drivers/clk/tegra/clk.h             |  1 +
>>>>    2 files changed, 16 insertions(+)
>>>>
>>>> diff --git a/drivers/clk/tegra/clk-tegra-fixed.c
>>>> b/drivers/clk/tegra/clk-tegra-fixed.c
>>>> index 8d91b2b191cf..7c6c8abfcde6 100644
>>>> --- a/drivers/clk/tegra/clk-tegra-fixed.c
>>>> +++ b/drivers/clk/tegra/clk-tegra-fixed.c
>>>> @@ -17,6 +17,10 @@
>>>>    #define OSC_CTRL            0x50
>>>>    #define OSC_CTRL_OSC_FREQ_SHIFT        28
>>>>    #define OSC_CTRL_PLL_REF_DIV_SHIFT    26
>>>> +#define OSC_CTRL_MASK            (0x3f2 |    \
>>>> +                    (0xf << OSC_CTRL_OSC_FREQ_SHIFT))
>>>> +
>>>> +static u32 osc_ctrl_ctx;
>>>>      int __init tegra_osc_clk_init(void __iomem *clk_base, struct
>>>> tegra_clk *clks,
>>>>                      unsigned long *input_freqs, unsigned int num,
>>>> @@ -29,6 +33,7 @@ int __init tegra_osc_clk_init(void __iomem
>>>> *clk_base, struct tegra_clk *clks,
>>>>        unsigned osc_idx;
>>>>          val = readl_relaxed(clk_base + OSC_CTRL);
>>>> +    osc_ctrl_ctx = val & OSC_CTRL_MASK;
>>>>        osc_idx = val >> OSC_CTRL_OSC_FREQ_SHIFT;
>>>>          if (osc_idx < num)
>>>> @@ -96,3 +101,13 @@ void __init tegra_fixed_clk_init(struct tegra_clk
>>>> *tegra_clks)
>>>>            *dt_clk = clk;
>>>>        }
>>>>    }
>>>> +
>>>> +void tegra_clk_osc_resume(void __iomem *clk_base)
>>>> +{
>>>> +    u32 val;
>>>> +
>>>> +    val = readl_relaxed(clk_base + OSC_CTRL) & ~OSC_CTRL_MASK;
>>>> +    val |= osc_ctrl_ctx;
>>>> +    writel_relaxed(val, clk_base + OSC_CTRL);
>>> Why a full raw u32 OSC_CTRL value couldn't be simply saved and restored?
>> Storing and restoring only required fields to avoid accidental
>> misconfiguration.
>>
>> OSC_CTRL register has other bits (PLL_REF_DIV) which are configured by
>> BR depending on OSC_FREQ and also setting PLL_REF_DIV while PLLS are in
>> use is not safe.
> I'm looking at the clk-driver sources and see that none of the Tegra
> drivers ever change the OSC_CTRL configuration, T30/114 even have
> #defines for the OSC_CTRL that are unused.
>
> So, this leads to a question.. does any bootloader really ever change
> the OSC_CTRL such that it differs after resume from suspend in
> comparison to the value at the time of kernel's booting up?

For Tegra210, bootloader programs OSC_CTRL register for drivestrength 
programming.

These settings need to be restored to the same on SC7 exit as they gets 
reset during SC7 entry.

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

* Re: [PATCH v7 16/20] arm64: tegra: Enable wake from deep sleep on RTC alarm
  2019-08-01 17:56         ` Sowjanya Komatineni
@ 2019-08-01 18:39           ` Dmitry Osipenko
  0 siblings, 0 replies; 67+ messages in thread
From: Dmitry Osipenko @ 2019-08-01 18:39 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree

01.08.2019 20:56, Sowjanya Komatineni пишет:
> 
> On 8/1/19 3:43 AM, Dmitry Osipenko wrote:
>> 01.08.2019 0:08, Sowjanya Komatineni пишет:
>>> On 7/31/19 4:04 AM, Dmitry Osipenko wrote:
>>>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>>>> This patch updates device tree for RTC and PMC to allow system wake
>>>>> from deep sleep on RTC alarm.
>>>>>
>>>>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>>>>> ---
>>>>>    arch/arm64/boot/dts/nvidia/tegra210.dtsi | 5 ++++-
>>>>>    1 file changed, 4 insertions(+), 1 deletion(-)
>>>>>
>>>>> diff --git a/arch/arm64/boot/dts/nvidia/tegra210.dtsi
>>>>> b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
>>>>> index 659753118e96..30a7c48385a2 100644
>>>>> --- a/arch/arm64/boot/dts/nvidia/tegra210.dtsi
>>>>> +++ b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
>>>>> @@ -768,7 +768,8 @@
>>>>>        rtc@7000e000 {
>>>>>            compatible = "nvidia,tegra210-rtc", "nvidia,tegra20-rtc";
>>>>>            reg = <0x0 0x7000e000 0x0 0x100>;
>>>>> -        interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
>>>>> +        interrupts = <16 IRQ_TYPE_LEVEL_HIGH>;
>>>>> +        interrupt-parent = <&pmc>;
>>>>>            clocks = <&tegra_car TEGRA210_CLK_RTC>;
>>>>>            clock-names = "rtc";
>>>>>        };
>>>>> @@ -778,6 +779,8 @@
>>>>>            reg = <0x0 0x7000e400 0x0 0x400>;
>>>>>            clocks = <&tegra_car TEGRA210_CLK_PCLK>, <&clk32k_in>;
>>>>>            clock-names = "pclk", "clk32k_in";
>>>>> +        #interrupt-cells = <2>;
>>>>> +        interrupt-controller;
>>>>>              powergates {
>>>>>                pd_audio: aud {
>>>>>
>>>> Is this a backwards-compatible change? Or it's not really worth to care
>>>> about the compatibility with older kernel versions, I'm not sure about
>>>> overall state of T210 in the upstream kernel.
>>> I don't think its required to be backwards-compatible as SC7 entry/exit
>>> implementation for T210 is with this patch series onwards..
>> The new device tree binary should work with older kernel versions, AFAIK
>> this is the upstream rule. But if kernel support isn't in a very good
>> shape and not much people are using it, then obviously it is not very
>> important.
> 
> Yes, my response to backwards-compatible was with respect to interrupt
> parent change as this will not be backward compatible and also there is
> no Tegra210 suspend/resume earlier. Other functionality wise, it is
> backward compatible.

Should be good then.

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

* Re: [PATCH v7 06/20] clk: tegra: Support for OSC context save and restore
  2019-08-01 18:06         ` Sowjanya Komatineni
@ 2019-08-01 18:42           ` Dmitry Osipenko
  0 siblings, 0 replies; 67+ messages in thread
From: Dmitry Osipenko @ 2019-08-01 18:42 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree

01.08.2019 21:06, Sowjanya Komatineni пишет:
> 
> On 8/1/19 3:53 AM, Dmitry Osipenko wrote:
>> 01.08.2019 0:04, Sowjanya Komatineni пишет:
>>> On 7/31/19 4:11 AM, Dmitry Osipenko wrote:
>>>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>>>> This patch adds support for saving OSC clock frequency and the
>>>>> drive-strength during OSC clock init and creates an API to restore
>>>>> OSC control register value from the saved context.
>>>>>
>>>>> This API is invoked by Tegra210 clock driver during system resume
>>>>> to restore the  OSC clock settings.
>>>>>
>>>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>>>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>>>>> ---
>>>>>    drivers/clk/tegra/clk-tegra-fixed.c | 15 +++++++++++++++
>>>>>    drivers/clk/tegra/clk.h             |  1 +
>>>>>    2 files changed, 16 insertions(+)
>>>>>
>>>>> diff --git a/drivers/clk/tegra/clk-tegra-fixed.c
>>>>> b/drivers/clk/tegra/clk-tegra-fixed.c
>>>>> index 8d91b2b191cf..7c6c8abfcde6 100644
>>>>> --- a/drivers/clk/tegra/clk-tegra-fixed.c
>>>>> +++ b/drivers/clk/tegra/clk-tegra-fixed.c
>>>>> @@ -17,6 +17,10 @@
>>>>>    #define OSC_CTRL            0x50
>>>>>    #define OSC_CTRL_OSC_FREQ_SHIFT        28
>>>>>    #define OSC_CTRL_PLL_REF_DIV_SHIFT    26
>>>>> +#define OSC_CTRL_MASK            (0x3f2 |    \
>>>>> +                    (0xf << OSC_CTRL_OSC_FREQ_SHIFT))
>>>>> +
>>>>> +static u32 osc_ctrl_ctx;
>>>>>      int __init tegra_osc_clk_init(void __iomem *clk_base, struct
>>>>> tegra_clk *clks,
>>>>>                      unsigned long *input_freqs, unsigned int num,
>>>>> @@ -29,6 +33,7 @@ int __init tegra_osc_clk_init(void __iomem
>>>>> *clk_base, struct tegra_clk *clks,
>>>>>        unsigned osc_idx;
>>>>>          val = readl_relaxed(clk_base + OSC_CTRL);
>>>>> +    osc_ctrl_ctx = val & OSC_CTRL_MASK;
>>>>>        osc_idx = val >> OSC_CTRL_OSC_FREQ_SHIFT;
>>>>>          if (osc_idx < num)
>>>>> @@ -96,3 +101,13 @@ void __init tegra_fixed_clk_init(struct tegra_clk
>>>>> *tegra_clks)
>>>>>            *dt_clk = clk;
>>>>>        }
>>>>>    }
>>>>> +
>>>>> +void tegra_clk_osc_resume(void __iomem *clk_base)
>>>>> +{
>>>>> +    u32 val;
>>>>> +
>>>>> +    val = readl_relaxed(clk_base + OSC_CTRL) & ~OSC_CTRL_MASK;
>>>>> +    val |= osc_ctrl_ctx;
>>>>> +    writel_relaxed(val, clk_base + OSC_CTRL);
>>>> Why a full raw u32 OSC_CTRL value couldn't be simply saved and
>>>> restored?
>>> Storing and restoring only required fields to avoid accidental
>>> misconfiguration.
>>>
>>> OSC_CTRL register has other bits (PLL_REF_DIV) which are configured by
>>> BR depending on OSC_FREQ and also setting PLL_REF_DIV while PLLS are in
>>> use is not safe.
>> I'm looking at the clk-driver sources and see that none of the Tegra
>> drivers ever change the OSC_CTRL configuration, T30/114 even have
>> #defines for the OSC_CTRL that are unused.
>>
>> So, this leads to a question.. does any bootloader really ever change
>> the OSC_CTRL such that it differs after resume from suspend in
>> comparison to the value at the time of kernel's booting up?
> 
> For Tegra210, bootloader programs OSC_CTRL register for drivestrength
> programming.
> 
> These settings need to be restored to the same on SC7 exit as they gets
> reset during SC7 entry.

Okay, thank you for the clarification.

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-08-01 17:58         ` Sowjanya Komatineni
@ 2019-08-01 19:00           ` Dmitry Osipenko
  2019-08-01 19:42             ` Sowjanya Komatineni
  0 siblings, 1 reply; 67+ messages in thread
From: Dmitry Osipenko @ 2019-08-01 19:00 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree

01.08.2019 20:58, Sowjanya Komatineni пишет:
> 
> On 7/31/19 4:09 PM, Sowjanya Komatineni wrote:
>>
>> On 7/31/19 3:44 AM, Dmitry Osipenko wrote:
>>> 31.07.2019 12:50, Dmitry Osipenko пишет:
>>>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>>>> This patch implements save and restore context for peripheral fixed
>>>>> clock ops, peripheral gate clock ops, sdmmc mux clock ops, and
>>>>> peripheral clock ops.
>>>>>
>>>>> During system suspend, core power goes off and looses the settings
>>>>> of the Tegra CAR controller registers.
>>>>>
>>>>> So during suspend entry clock and reset state of peripherals is saved
>>>>> and on resume they are restored to have clocks back to same rate and
>>>>> state as before suspend.
>>>>>
>>>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>>>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>>>>> ---
>>>>>   drivers/clk/tegra/clk-periph-fixed.c | 33
>>>>> ++++++++++++++++++++++++++++++++
>>>>>   drivers/clk/tegra/clk-periph-gate.c  | 34
>>>>> +++++++++++++++++++++++++++++++++
>>>>>   drivers/clk/tegra/clk-periph.c       | 37
>>>>> ++++++++++++++++++++++++++++++++++++
>>>>>   drivers/clk/tegra/clk-sdmmc-mux.c    | 28
>>>>> +++++++++++++++++++++++++++
>>>>>   drivers/clk/tegra/clk.h              |  6 ++++++
>>>>>   5 files changed, 138 insertions(+)
>>>>>
>>>>> diff --git a/drivers/clk/tegra/clk-periph-fixed.c
>>>>> b/drivers/clk/tegra/clk-periph-fixed.c
>>>>> index c088e7a280df..21b24530fa00 100644
>>>>> --- a/drivers/clk/tegra/clk-periph-fixed.c
>>>>> +++ b/drivers/clk/tegra/clk-periph-fixed.c
>>>>> @@ -60,11 +60,44 @@ tegra_clk_periph_fixed_recalc_rate(struct
>>>>> clk_hw *hw,
>>>>>       return (unsigned long)rate;
>>>>>   }
>>>>>   +static int tegra_clk_periph_fixed_save_context(struct clk_hw *hw)
>>>>> +{
>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>> to_tegra_clk_periph_fixed(hw);
>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>> +
>>>>> +    fixed->enb_ctx = readl_relaxed(fixed->base +
>>>>> fixed->regs->enb_reg) &
>>>>> +             mask;
>>>>> +    fixed->rst_ctx = readl_relaxed(fixed->base +
>>>>> fixed->regs->rst_reg) &
>>>>> +             mask;
>>>>> +
>>>>> +    return 0;
>>>>> +}
>>>>> +
>>>>> +static void tegra_clk_periph_fixed_restore_context(struct clk_hw *hw)
>>>>> +{
>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>> to_tegra_clk_periph_fixed(hw);
>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>> +
>>>>> +    if (fixed->enb_ctx)
>>>>> +        writel_relaxed(mask, fixed->base + fixed->regs->enb_set_reg);
>>>>> +    else
>>>>> +        writel_relaxed(mask, fixed->base + fixed->regs->enb_clr_reg);
>>>>> +
>>>>> +    udelay(2);
>>>>> +
>>>>> +    if (!fixed->rst_ctx) {
>>>>> +        udelay(5); /* reset propogation delay */
>>>>> +        writel_relaxed(mask, fixed->base + fixed->regs->rst_reg);
>>>>> +    }
>>>>> +}
>>>>> +
>>>>>   static const struct clk_ops tegra_clk_periph_fixed_ops = {
>>>>>       .is_enabled = tegra_clk_periph_fixed_is_enabled,
>>>>>       .enable = tegra_clk_periph_fixed_enable,
>>>>>       .disable = tegra_clk_periph_fixed_disable,
>>>>>       .recalc_rate = tegra_clk_periph_fixed_recalc_rate,
>>>>> +    .save_context = tegra_clk_periph_fixed_save_context,
>>>>> +    .restore_context = tegra_clk_periph_fixed_restore_context,
>>>>>   };
>>>>>     struct clk *tegra_clk_register_periph_fixed(const char *name,
>>>>> diff --git a/drivers/clk/tegra/clk-periph-gate.c
>>>>> b/drivers/clk/tegra/clk-periph-gate.c
>>>>> index 4b31beefc9fc..6ba5b08e0787 100644
>>>>> --- a/drivers/clk/tegra/clk-periph-gate.c
>>>>> +++ b/drivers/clk/tegra/clk-periph-gate.c
>>>>> @@ -25,6 +25,8 @@ static DEFINE_SPINLOCK(periph_ref_lock);
>>>>>     #define read_rst(gate) \
>>>>>       readl_relaxed(gate->clk_base + (gate->regs->rst_reg))
>>>>> +#define write_rst_set(val, gate) \
>>>>> +    writel_relaxed(val, gate->clk_base + (gate->regs->rst_set_reg))
>>>>>   #define write_rst_clr(val, gate) \
>>>>>       writel_relaxed(val, gate->clk_base + (gate->regs->rst_clr_reg))
>>>>>   @@ -110,10 +112,42 @@ static void clk_periph_disable(struct
>>>>> clk_hw *hw)
>>>>>       spin_unlock_irqrestore(&periph_ref_lock, flags);
>>>>>   }
>>>>>   +static int clk_periph_gate_save_context(struct clk_hw *hw)
>>>>> +{
>>>>> +    struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
>>>>> +
>>>>> +    gate->clk_state_ctx = read_enb(gate) & periph_clk_to_bit(gate);
>>>>> +    gate->rst_state_ctx = read_rst(gate) & periph_clk_to_bit(gate);
>>>>> +
>>>>> +    return 0;
>>>>> +}
>>>>> +
>>>>> +static void clk_periph_gate_restore_context(struct clk_hw *hw)
>>>>> +{
>>>>> +    struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
>>>>> +
>>>>> +    if (gate->clk_state_ctx)
>>>>> +        write_enb_set(periph_clk_to_bit(gate), gate);
>>>>> +    else
>>>>> +        write_enb_clr(periph_clk_to_bit(gate), gate);
>>>>> +
>>>>> +    udelay(5);
>>>>> +
>>>>> +    if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
>>>>> +        !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
>>>>> +        if (gate->rst_state_ctx)
>>>>> +            write_rst_set(periph_clk_to_bit(gate), gate);
>>>>> +        else
>>>>> +            write_rst_clr(periph_clk_to_bit(gate), gate);
>>>>> +    }
>>>>> +}
>>>>> +
>>>>>   const struct clk_ops tegra_clk_periph_gate_ops = {
>>>>>       .is_enabled = clk_periph_is_enabled,
>>>>>       .enable = clk_periph_enable,
>>>>>       .disable = clk_periph_disable,
>>>>> +    .save_context = clk_periph_gate_save_context,
>>>>> +    .restore_context = clk_periph_gate_restore_context,
>>>>>   };
>>>>>     struct clk *tegra_clk_register_periph_gate(const char *name,
>>>>> diff --git a/drivers/clk/tegra/clk-periph.c
>>>>> b/drivers/clk/tegra/clk-periph.c
>>>>> index 58437da25156..06fb62955768 100644
>>>>> --- a/drivers/clk/tegra/clk-periph.c
>>>>> +++ b/drivers/clk/tegra/clk-periph.c
>>>>> @@ -99,6 +99,37 @@ static void clk_periph_disable(struct clk_hw *hw)
>>>>>       gate_ops->disable(gate_hw);
>>>>>   }
>>>>>   +static int clk_periph_save_context(struct clk_hw *hw)
>>>>> +{
>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>> +
>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
>>>>> +        gate_ops->save_context(gate_hw);
>>>>> +
>>>>> +    periph->parent_ctx = clk_periph_get_parent(hw);
>>>>> +
>>>>> +    return 0;
>>>>> +}
>>>>> +
>>>>> +static void clk_periph_restore_context(struct clk_hw *hw)
>>>>> +{
>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>> +    const struct clk_ops *div_ops = periph->div_ops;
>>>>> +    struct clk_hw *div_hw = &periph->divider.hw;
>>>>> +
>>>>> +    clk_periph_set_parent(hw, periph->parent_ctx);
>>>>> +
>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
>>>>> +        div_ops->restore_context(div_hw);
>>>> Could you please point to where the divider's save_context() happens?
>>>> Because I can't see it.
>>> Ah, I now see that there is no need to save the dividers context because
>>> clk itself has enough info that is needed for the context's restoring
>>> (like I pointed in the review to v6).
>>>
>>> Looks like you could also implement a new clk_hw_get_parent_index()
>>> generic helper to get the index instead of storing it manually.
>>
>> clk_periph_get_parent basically invokes existing clk_mux_ops
>> get_parent() which is then saved in tegra_clk_periph.
>>
>> All existing drivers are using directly get_parent() from clk_mux
>> which actually gets index from the register read.
>>
>> To have this more generic w.r.t save/restore context point of view,
>> probably instead of implementing new get_parent_index helper, I think
>> its better to implement save_context and restore_context to
>> clk_mux_ops along with creating parent_index field into clk_mux to
>> cache index during set_parent.
>>
>> So we just need to invoke mux_ops save_context and restore_context.
>>
> I hope its ok to add save/restore context to clk_mux_ops to be more
> generic w.r.t save/restore context rather than get_parent_index API.
> Please confirm if you agree.

Sounds like a good idea. I see that there is a 'restoring' helper for
the generic clk_gate, seems something similar could be done for the
clk_mux. And looks like anyway you'll need to associate the parent clock
with the hw index in order to restore the muxing.

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-08-01 19:00           ` Dmitry Osipenko
@ 2019-08-01 19:42             ` Sowjanya Komatineni
  2019-08-01 20:17               ` Dmitry Osipenko
  0 siblings, 1 reply; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-08-01 19:42 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree


On 8/1/19 12:00 PM, Dmitry Osipenko wrote:
> 01.08.2019 20:58, Sowjanya Komatineni пишет:
>> On 7/31/19 4:09 PM, Sowjanya Komatineni wrote:
>>> On 7/31/19 3:44 AM, Dmitry Osipenko wrote:
>>>> 31.07.2019 12:50, Dmitry Osipenko пишет:
>>>>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>>>>> This patch implements save and restore context for peripheral fixed
>>>>>> clock ops, peripheral gate clock ops, sdmmc mux clock ops, and
>>>>>> peripheral clock ops.
>>>>>>
>>>>>> During system suspend, core power goes off and looses the settings
>>>>>> of the Tegra CAR controller registers.
>>>>>>
>>>>>> So during suspend entry clock and reset state of peripherals is saved
>>>>>> and on resume they are restored to have clocks back to same rate and
>>>>>> state as before suspend.
>>>>>>
>>>>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>>>>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>>>>>> ---
>>>>>>    drivers/clk/tegra/clk-periph-fixed.c | 33
>>>>>> ++++++++++++++++++++++++++++++++
>>>>>>    drivers/clk/tegra/clk-periph-gate.c  | 34
>>>>>> +++++++++++++++++++++++++++++++++
>>>>>>    drivers/clk/tegra/clk-periph.c       | 37
>>>>>> ++++++++++++++++++++++++++++++++++++
>>>>>>    drivers/clk/tegra/clk-sdmmc-mux.c    | 28
>>>>>> +++++++++++++++++++++++++++
>>>>>>    drivers/clk/tegra/clk.h              |  6 ++++++
>>>>>>    5 files changed, 138 insertions(+)
>>>>>>
>>>>>> diff --git a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>> b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>> index c088e7a280df..21b24530fa00 100644
>>>>>> --- a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>> +++ b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>> @@ -60,11 +60,44 @@ tegra_clk_periph_fixed_recalc_rate(struct
>>>>>> clk_hw *hw,
>>>>>>        return (unsigned long)rate;
>>>>>>    }
>>>>>>    +static int tegra_clk_periph_fixed_save_context(struct clk_hw *hw)
>>>>>> +{
>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>> +
>>>>>> +    fixed->enb_ctx = readl_relaxed(fixed->base +
>>>>>> fixed->regs->enb_reg) &
>>>>>> +             mask;
>>>>>> +    fixed->rst_ctx = readl_relaxed(fixed->base +
>>>>>> fixed->regs->rst_reg) &
>>>>>> +             mask;
>>>>>> +
>>>>>> +    return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static void tegra_clk_periph_fixed_restore_context(struct clk_hw *hw)
>>>>>> +{
>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>> +
>>>>>> +    if (fixed->enb_ctx)
>>>>>> +        writel_relaxed(mask, fixed->base + fixed->regs->enb_set_reg);
>>>>>> +    else
>>>>>> +        writel_relaxed(mask, fixed->base + fixed->regs->enb_clr_reg);
>>>>>> +
>>>>>> +    udelay(2);
>>>>>> +
>>>>>> +    if (!fixed->rst_ctx) {
>>>>>> +        udelay(5); /* reset propogation delay */
>>>>>> +        writel_relaxed(mask, fixed->base + fixed->regs->rst_reg);
>>>>>> +    }
>>>>>> +}
>>>>>> +
>>>>>>    static const struct clk_ops tegra_clk_periph_fixed_ops = {
>>>>>>        .is_enabled = tegra_clk_periph_fixed_is_enabled,
>>>>>>        .enable = tegra_clk_periph_fixed_enable,
>>>>>>        .disable = tegra_clk_periph_fixed_disable,
>>>>>>        .recalc_rate = tegra_clk_periph_fixed_recalc_rate,
>>>>>> +    .save_context = tegra_clk_periph_fixed_save_context,
>>>>>> +    .restore_context = tegra_clk_periph_fixed_restore_context,
>>>>>>    };
>>>>>>      struct clk *tegra_clk_register_periph_fixed(const char *name,
>>>>>> diff --git a/drivers/clk/tegra/clk-periph-gate.c
>>>>>> b/drivers/clk/tegra/clk-periph-gate.c
>>>>>> index 4b31beefc9fc..6ba5b08e0787 100644
>>>>>> --- a/drivers/clk/tegra/clk-periph-gate.c
>>>>>> +++ b/drivers/clk/tegra/clk-periph-gate.c
>>>>>> @@ -25,6 +25,8 @@ static DEFINE_SPINLOCK(periph_ref_lock);
>>>>>>      #define read_rst(gate) \
>>>>>>        readl_relaxed(gate->clk_base + (gate->regs->rst_reg))
>>>>>> +#define write_rst_set(val, gate) \
>>>>>> +    writel_relaxed(val, gate->clk_base + (gate->regs->rst_set_reg))
>>>>>>    #define write_rst_clr(val, gate) \
>>>>>>        writel_relaxed(val, gate->clk_base + (gate->regs->rst_clr_reg))
>>>>>>    @@ -110,10 +112,42 @@ static void clk_periph_disable(struct
>>>>>> clk_hw *hw)
>>>>>>        spin_unlock_irqrestore(&periph_ref_lock, flags);
>>>>>>    }
>>>>>>    +static int clk_periph_gate_save_context(struct clk_hw *hw)
>>>>>> +{
>>>>>> +    struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
>>>>>> +
>>>>>> +    gate->clk_state_ctx = read_enb(gate) & periph_clk_to_bit(gate);
>>>>>> +    gate->rst_state_ctx = read_rst(gate) & periph_clk_to_bit(gate);
>>>>>> +
>>>>>> +    return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static void clk_periph_gate_restore_context(struct clk_hw *hw)
>>>>>> +{
>>>>>> +    struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
>>>>>> +
>>>>>> +    if (gate->clk_state_ctx)
>>>>>> +        write_enb_set(periph_clk_to_bit(gate), gate);
>>>>>> +    else
>>>>>> +        write_enb_clr(periph_clk_to_bit(gate), gate);
>>>>>> +
>>>>>> +    udelay(5);
>>>>>> +
>>>>>> +    if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
>>>>>> +        !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
>>>>>> +        if (gate->rst_state_ctx)
>>>>>> +            write_rst_set(periph_clk_to_bit(gate), gate);
>>>>>> +        else
>>>>>> +            write_rst_clr(periph_clk_to_bit(gate), gate);
>>>>>> +    }
>>>>>> +}
>>>>>> +
>>>>>>    const struct clk_ops tegra_clk_periph_gate_ops = {
>>>>>>        .is_enabled = clk_periph_is_enabled,
>>>>>>        .enable = clk_periph_enable,
>>>>>>        .disable = clk_periph_disable,
>>>>>> +    .save_context = clk_periph_gate_save_context,
>>>>>> +    .restore_context = clk_periph_gate_restore_context,
>>>>>>    };
>>>>>>      struct clk *tegra_clk_register_periph_gate(const char *name,
>>>>>> diff --git a/drivers/clk/tegra/clk-periph.c
>>>>>> b/drivers/clk/tegra/clk-periph.c
>>>>>> index 58437da25156..06fb62955768 100644
>>>>>> --- a/drivers/clk/tegra/clk-periph.c
>>>>>> +++ b/drivers/clk/tegra/clk-periph.c
>>>>>> @@ -99,6 +99,37 @@ static void clk_periph_disable(struct clk_hw *hw)
>>>>>>        gate_ops->disable(gate_hw);
>>>>>>    }
>>>>>>    +static int clk_periph_save_context(struct clk_hw *hw)
>>>>>> +{
>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>> +
>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
>>>>>> +        gate_ops->save_context(gate_hw);
>>>>>> +
>>>>>> +    periph->parent_ctx = clk_periph_get_parent(hw);
>>>>>> +
>>>>>> +    return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static void clk_periph_restore_context(struct clk_hw *hw)
>>>>>> +{
>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>> +    const struct clk_ops *div_ops = periph->div_ops;
>>>>>> +    struct clk_hw *div_hw = &periph->divider.hw;
>>>>>> +
>>>>>> +    clk_periph_set_parent(hw, periph->parent_ctx);
>>>>>> +
>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
>>>>>> +        div_ops->restore_context(div_hw);
>>>>> Could you please point to where the divider's save_context() happens?
>>>>> Because I can't see it.
>>>> Ah, I now see that there is no need to save the dividers context because
>>>> clk itself has enough info that is needed for the context's restoring
>>>> (like I pointed in the review to v6).
>>>>
>>>> Looks like you could also implement a new clk_hw_get_parent_index()
>>>> generic helper to get the index instead of storing it manually.
>>> clk_periph_get_parent basically invokes existing clk_mux_ops
>>> get_parent() which is then saved in tegra_clk_periph.
>>>
>>> All existing drivers are using directly get_parent() from clk_mux
>>> which actually gets index from the register read.
>>>
>>> To have this more generic w.r.t save/restore context point of view,
>>> probably instead of implementing new get_parent_index helper, I think
>>> its better to implement save_context and restore_context to
>>> clk_mux_ops along with creating parent_index field into clk_mux to
>>> cache index during set_parent.
>>>
>>> So we just need to invoke mux_ops save_context and restore_context.
>>>
>> I hope its ok to add save/restore context to clk_mux_ops to be more
>> generic w.r.t save/restore context rather than get_parent_index API.
>> Please confirm if you agree.
> Sounds like a good idea. I see that there is a 'restoring' helper for
> the generic clk_gate, seems something similar could be done for the
> clk_mux. And looks like anyway you'll need to associate the parent clock
> with the hw index in order to restore the muxing.

by 'restoring' helper for generic clk_gate, are you referring to 
clk_gate_restore_context API?

clk_gate_restore_context is API that's any clk drivers can use for 
clk_gate operation restore for custom gate clk_ops.

But clk-periph is directly using generic clk_mux ops from clk_mux so I 
think we should add .restore_context to clk_mux_ops and then during 
clk-periph restore need to invoke mux_ops->restore_context.

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-08-01 19:42             ` Sowjanya Komatineni
@ 2019-08-01 20:17               ` Dmitry Osipenko
  2019-08-01 20:31                 ` Sowjanya Komatineni
  0 siblings, 1 reply; 67+ messages in thread
From: Dmitry Osipenko @ 2019-08-01 20:17 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree

01.08.2019 22:42, Sowjanya Komatineni пишет:
> 
> On 8/1/19 12:00 PM, Dmitry Osipenko wrote:
>> 01.08.2019 20:58, Sowjanya Komatineni пишет:
>>> On 7/31/19 4:09 PM, Sowjanya Komatineni wrote:
>>>> On 7/31/19 3:44 AM, Dmitry Osipenko wrote:
>>>>> 31.07.2019 12:50, Dmitry Osipenko пишет:
>>>>>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>>>>>> This patch implements save and restore context for peripheral fixed
>>>>>>> clock ops, peripheral gate clock ops, sdmmc mux clock ops, and
>>>>>>> peripheral clock ops.
>>>>>>>
>>>>>>> During system suspend, core power goes off and looses the settings
>>>>>>> of the Tegra CAR controller registers.
>>>>>>>
>>>>>>> So during suspend entry clock and reset state of peripherals is
>>>>>>> saved
>>>>>>> and on resume they are restored to have clocks back to same rate and
>>>>>>> state as before suspend.
>>>>>>>
>>>>>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>>>>>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>>>>>>> ---
>>>>>>>    drivers/clk/tegra/clk-periph-fixed.c | 33
>>>>>>> ++++++++++++++++++++++++++++++++
>>>>>>>    drivers/clk/tegra/clk-periph-gate.c  | 34
>>>>>>> +++++++++++++++++++++++++++++++++
>>>>>>>    drivers/clk/tegra/clk-periph.c       | 37
>>>>>>> ++++++++++++++++++++++++++++++++++++
>>>>>>>    drivers/clk/tegra/clk-sdmmc-mux.c    | 28
>>>>>>> +++++++++++++++++++++++++++
>>>>>>>    drivers/clk/tegra/clk.h              |  6 ++++++
>>>>>>>    5 files changed, 138 insertions(+)
>>>>>>>
>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>> b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>> index c088e7a280df..21b24530fa00 100644
>>>>>>> --- a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>> +++ b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>> @@ -60,11 +60,44 @@ tegra_clk_periph_fixed_recalc_rate(struct
>>>>>>> clk_hw *hw,
>>>>>>>        return (unsigned long)rate;
>>>>>>>    }
>>>>>>>    +static int tegra_clk_periph_fixed_save_context(struct clk_hw
>>>>>>> *hw)
>>>>>>> +{
>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>> +
>>>>>>> +    fixed->enb_ctx = readl_relaxed(fixed->base +
>>>>>>> fixed->regs->enb_reg) &
>>>>>>> +             mask;
>>>>>>> +    fixed->rst_ctx = readl_relaxed(fixed->base +
>>>>>>> fixed->regs->rst_reg) &
>>>>>>> +             mask;
>>>>>>> +
>>>>>>> +    return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void tegra_clk_periph_fixed_restore_context(struct clk_hw
>>>>>>> *hw)
>>>>>>> +{
>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>> +
>>>>>>> +    if (fixed->enb_ctx)
>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>> fixed->regs->enb_set_reg);
>>>>>>> +    else
>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>> fixed->regs->enb_clr_reg);
>>>>>>> +
>>>>>>> +    udelay(2);
>>>>>>> +
>>>>>>> +    if (!fixed->rst_ctx) {
>>>>>>> +        udelay(5); /* reset propogation delay */
>>>>>>> +        writel_relaxed(mask, fixed->base + fixed->regs->rst_reg);
>>>>>>> +    }
>>>>>>> +}
>>>>>>> +
>>>>>>>    static const struct clk_ops tegra_clk_periph_fixed_ops = {
>>>>>>>        .is_enabled = tegra_clk_periph_fixed_is_enabled,
>>>>>>>        .enable = tegra_clk_periph_fixed_enable,
>>>>>>>        .disable = tegra_clk_periph_fixed_disable,
>>>>>>>        .recalc_rate = tegra_clk_periph_fixed_recalc_rate,
>>>>>>> +    .save_context = tegra_clk_periph_fixed_save_context,
>>>>>>> +    .restore_context = tegra_clk_periph_fixed_restore_context,
>>>>>>>    };
>>>>>>>      struct clk *tegra_clk_register_periph_fixed(const char *name,
>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>> b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>> index 4b31beefc9fc..6ba5b08e0787 100644
>>>>>>> --- a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>> +++ b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>> @@ -25,6 +25,8 @@ static DEFINE_SPINLOCK(periph_ref_lock);
>>>>>>>      #define read_rst(gate) \
>>>>>>>        readl_relaxed(gate->clk_base + (gate->regs->rst_reg))
>>>>>>> +#define write_rst_set(val, gate) \
>>>>>>> +    writel_relaxed(val, gate->clk_base + (gate->regs->rst_set_reg))
>>>>>>>    #define write_rst_clr(val, gate) \
>>>>>>>        writel_relaxed(val, gate->clk_base +
>>>>>>> (gate->regs->rst_clr_reg))
>>>>>>>    @@ -110,10 +112,42 @@ static void clk_periph_disable(struct
>>>>>>> clk_hw *hw)
>>>>>>>        spin_unlock_irqrestore(&periph_ref_lock, flags);
>>>>>>>    }
>>>>>>>    +static int clk_periph_gate_save_context(struct clk_hw *hw)
>>>>>>> +{
>>>>>>> +    struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
>>>>>>> +
>>>>>>> +    gate->clk_state_ctx = read_enb(gate) & periph_clk_to_bit(gate);
>>>>>>> +    gate->rst_state_ctx = read_rst(gate) & periph_clk_to_bit(gate);
>>>>>>> +
>>>>>>> +    return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void clk_periph_gate_restore_context(struct clk_hw *hw)
>>>>>>> +{
>>>>>>> +    struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
>>>>>>> +
>>>>>>> +    if (gate->clk_state_ctx)
>>>>>>> +        write_enb_set(periph_clk_to_bit(gate), gate);
>>>>>>> +    else
>>>>>>> +        write_enb_clr(periph_clk_to_bit(gate), gate);
>>>>>>> +
>>>>>>> +    udelay(5);
>>>>>>> +
>>>>>>> +    if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
>>>>>>> +        !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
>>>>>>> +        if (gate->rst_state_ctx)
>>>>>>> +            write_rst_set(periph_clk_to_bit(gate), gate);
>>>>>>> +        else
>>>>>>> +            write_rst_clr(periph_clk_to_bit(gate), gate);
>>>>>>> +    }
>>>>>>> +}
>>>>>>> +
>>>>>>>    const struct clk_ops tegra_clk_periph_gate_ops = {
>>>>>>>        .is_enabled = clk_periph_is_enabled,
>>>>>>>        .enable = clk_periph_enable,
>>>>>>>        .disable = clk_periph_disable,
>>>>>>> +    .save_context = clk_periph_gate_save_context,
>>>>>>> +    .restore_context = clk_periph_gate_restore_context,
>>>>>>>    };
>>>>>>>      struct clk *tegra_clk_register_periph_gate(const char *name,
>>>>>>> diff --git a/drivers/clk/tegra/clk-periph.c
>>>>>>> b/drivers/clk/tegra/clk-periph.c
>>>>>>> index 58437da25156..06fb62955768 100644
>>>>>>> --- a/drivers/clk/tegra/clk-periph.c
>>>>>>> +++ b/drivers/clk/tegra/clk-periph.c
>>>>>>> @@ -99,6 +99,37 @@ static void clk_periph_disable(struct clk_hw *hw)
>>>>>>>        gate_ops->disable(gate_hw);
>>>>>>>    }
>>>>>>>    +static int clk_periph_save_context(struct clk_hw *hw)
>>>>>>> +{
>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>> +
>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
>>>>>>> +        gate_ops->save_context(gate_hw);
>>>>>>> +
>>>>>>> +    periph->parent_ctx = clk_periph_get_parent(hw);
>>>>>>> +
>>>>>>> +    return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void clk_periph_restore_context(struct clk_hw *hw)
>>>>>>> +{
>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>> +    const struct clk_ops *div_ops = periph->div_ops;
>>>>>>> +    struct clk_hw *div_hw = &periph->divider.hw;
>>>>>>> +
>>>>>>> +    clk_periph_set_parent(hw, periph->parent_ctx);
>>>>>>> +
>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
>>>>>>> +        div_ops->restore_context(div_hw);
>>>>>> Could you please point to where the divider's save_context() happens?
>>>>>> Because I can't see it.
>>>>> Ah, I now see that there is no need to save the dividers context
>>>>> because
>>>>> clk itself has enough info that is needed for the context's restoring
>>>>> (like I pointed in the review to v6).
>>>>>
>>>>> Looks like you could also implement a new clk_hw_get_parent_index()
>>>>> generic helper to get the index instead of storing it manually.
>>>> clk_periph_get_parent basically invokes existing clk_mux_ops
>>>> get_parent() which is then saved in tegra_clk_periph.
>>>>
>>>> All existing drivers are using directly get_parent() from clk_mux
>>>> which actually gets index from the register read.
>>>>
>>>> To have this more generic w.r.t save/restore context point of view,
>>>> probably instead of implementing new get_parent_index helper, I think
>>>> its better to implement save_context and restore_context to
>>>> clk_mux_ops along with creating parent_index field into clk_mux to
>>>> cache index during set_parent.
>>>>
>>>> So we just need to invoke mux_ops save_context and restore_context.
>>>>
>>> I hope its ok to add save/restore context to clk_mux_ops to be more
>>> generic w.r.t save/restore context rather than get_parent_index API.
>>> Please confirm if you agree.
>> Sounds like a good idea. I see that there is a 'restoring' helper for
>> the generic clk_gate, seems something similar could be done for the
>> clk_mux. And looks like anyway you'll need to associate the parent clock
>> with the hw index in order to restore the muxing.
> 
> by 'restoring' helper for generic clk_gate, are you referring to
> clk_gate_restore_context API?

Yes.

> clk_gate_restore_context is API that's any clk drivers can use for
> clk_gate operation restore for custom gate clk_ops.
> 
> But clk-periph is directly using generic clk_mux ops from clk_mux so I
> think we should add .restore_context to clk_mux_ops and then during
> clk-periph restore need to invoke mux_ops->restore_context.

I'm not sure whether it will be good for every driver that uses generic
clk_mux ops. Should be more flexible to have a generic helper function
that any driver could use in order to restore the clock's parent.

The clk-periph restoring also includes case of combining divider and
parent restoring, so generic helper could be useful in that case as well.

It also looks like you could actually use the clk_gate_restore_context()
instead of manually saving the clock's enable-state, couldn't you?

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-08-01 20:17               ` Dmitry Osipenko
@ 2019-08-01 20:31                 ` Sowjanya Komatineni
  2019-08-01 20:54                   ` Dmitry Osipenko
  0 siblings, 1 reply; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-08-01 20:31 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree


On 8/1/19 1:17 PM, Dmitry Osipenko wrote:
> 01.08.2019 22:42, Sowjanya Komatineni пишет:
>> On 8/1/19 12:00 PM, Dmitry Osipenko wrote:
>>> 01.08.2019 20:58, Sowjanya Komatineni пишет:
>>>> On 7/31/19 4:09 PM, Sowjanya Komatineni wrote:
>>>>> On 7/31/19 3:44 AM, Dmitry Osipenko wrote:
>>>>>> 31.07.2019 12:50, Dmitry Osipenko пишет:
>>>>>>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>>>>>>> This patch implements save and restore context for peripheral fixed
>>>>>>>> clock ops, peripheral gate clock ops, sdmmc mux clock ops, and
>>>>>>>> peripheral clock ops.
>>>>>>>>
>>>>>>>> During system suspend, core power goes off and looses the settings
>>>>>>>> of the Tegra CAR controller registers.
>>>>>>>>
>>>>>>>> So during suspend entry clock and reset state of peripherals is
>>>>>>>> saved
>>>>>>>> and on resume they are restored to have clocks back to same rate and
>>>>>>>> state as before suspend.
>>>>>>>>
>>>>>>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>>>>>>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>>>>>>>> ---
>>>>>>>>     drivers/clk/tegra/clk-periph-fixed.c | 33
>>>>>>>> ++++++++++++++++++++++++++++++++
>>>>>>>>     drivers/clk/tegra/clk-periph-gate.c  | 34
>>>>>>>> +++++++++++++++++++++++++++++++++
>>>>>>>>     drivers/clk/tegra/clk-periph.c       | 37
>>>>>>>> ++++++++++++++++++++++++++++++++++++
>>>>>>>>     drivers/clk/tegra/clk-sdmmc-mux.c    | 28
>>>>>>>> +++++++++++++++++++++++++++
>>>>>>>>     drivers/clk/tegra/clk.h              |  6 ++++++
>>>>>>>>     5 files changed, 138 insertions(+)
>>>>>>>>
>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>> b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>> index c088e7a280df..21b24530fa00 100644
>>>>>>>> --- a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>> @@ -60,11 +60,44 @@ tegra_clk_periph_fixed_recalc_rate(struct
>>>>>>>> clk_hw *hw,
>>>>>>>>         return (unsigned long)rate;
>>>>>>>>     }
>>>>>>>>     +static int tegra_clk_periph_fixed_save_context(struct clk_hw
>>>>>>>> *hw)
>>>>>>>> +{
>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>> +
>>>>>>>> +    fixed->enb_ctx = readl_relaxed(fixed->base +
>>>>>>>> fixed->regs->enb_reg) &
>>>>>>>> +             mask;
>>>>>>>> +    fixed->rst_ctx = readl_relaxed(fixed->base +
>>>>>>>> fixed->regs->rst_reg) &
>>>>>>>> +             mask;
>>>>>>>> +
>>>>>>>> +    return 0;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static void tegra_clk_periph_fixed_restore_context(struct clk_hw
>>>>>>>> *hw)
>>>>>>>> +{
>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>> +
>>>>>>>> +    if (fixed->enb_ctx)
>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>> fixed->regs->enb_set_reg);
>>>>>>>> +    else
>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>> fixed->regs->enb_clr_reg);
>>>>>>>> +
>>>>>>>> +    udelay(2);
>>>>>>>> +
>>>>>>>> +    if (!fixed->rst_ctx) {
>>>>>>>> +        udelay(5); /* reset propogation delay */
>>>>>>>> +        writel_relaxed(mask, fixed->base + fixed->regs->rst_reg);
>>>>>>>> +    }
>>>>>>>> +}
>>>>>>>> +
>>>>>>>>     static const struct clk_ops tegra_clk_periph_fixed_ops = {
>>>>>>>>         .is_enabled = tegra_clk_periph_fixed_is_enabled,
>>>>>>>>         .enable = tegra_clk_periph_fixed_enable,
>>>>>>>>         .disable = tegra_clk_periph_fixed_disable,
>>>>>>>>         .recalc_rate = tegra_clk_periph_fixed_recalc_rate,
>>>>>>>> +    .save_context = tegra_clk_periph_fixed_save_context,
>>>>>>>> +    .restore_context = tegra_clk_periph_fixed_restore_context,
>>>>>>>>     };
>>>>>>>>       struct clk *tegra_clk_register_periph_fixed(const char *name,
>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>> b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>> index 4b31beefc9fc..6ba5b08e0787 100644
>>>>>>>> --- a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>> @@ -25,6 +25,8 @@ static DEFINE_SPINLOCK(periph_ref_lock);
>>>>>>>>       #define read_rst(gate) \
>>>>>>>>         readl_relaxed(gate->clk_base + (gate->regs->rst_reg))
>>>>>>>> +#define write_rst_set(val, gate) \
>>>>>>>> +    writel_relaxed(val, gate->clk_base + (gate->regs->rst_set_reg))
>>>>>>>>     #define write_rst_clr(val, gate) \
>>>>>>>>         writel_relaxed(val, gate->clk_base +
>>>>>>>> (gate->regs->rst_clr_reg))
>>>>>>>>     @@ -110,10 +112,42 @@ static void clk_periph_disable(struct
>>>>>>>> clk_hw *hw)
>>>>>>>>         spin_unlock_irqrestore(&periph_ref_lock, flags);
>>>>>>>>     }
>>>>>>>>     +static int clk_periph_gate_save_context(struct clk_hw *hw)
>>>>>>>> +{
>>>>>>>> +    struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
>>>>>>>> +
>>>>>>>> +    gate->clk_state_ctx = read_enb(gate) & periph_clk_to_bit(gate);
>>>>>>>> +    gate->rst_state_ctx = read_rst(gate) & periph_clk_to_bit(gate);
>>>>>>>> +
>>>>>>>> +    return 0;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static void clk_periph_gate_restore_context(struct clk_hw *hw)
>>>>>>>> +{
>>>>>>>> +    struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
>>>>>>>> +
>>>>>>>> +    if (gate->clk_state_ctx)
>>>>>>>> +        write_enb_set(periph_clk_to_bit(gate), gate);
>>>>>>>> +    else
>>>>>>>> +        write_enb_clr(periph_clk_to_bit(gate), gate);
>>>>>>>> +
>>>>>>>> +    udelay(5);
>>>>>>>> +
>>>>>>>> +    if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
>>>>>>>> +        !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
>>>>>>>> +        if (gate->rst_state_ctx)
>>>>>>>> +            write_rst_set(periph_clk_to_bit(gate), gate);
>>>>>>>> +        else
>>>>>>>> +            write_rst_clr(periph_clk_to_bit(gate), gate);
>>>>>>>> +    }
>>>>>>>> +}
>>>>>>>> +
>>>>>>>>     const struct clk_ops tegra_clk_periph_gate_ops = {
>>>>>>>>         .is_enabled = clk_periph_is_enabled,
>>>>>>>>         .enable = clk_periph_enable,
>>>>>>>>         .disable = clk_periph_disable,
>>>>>>>> +    .save_context = clk_periph_gate_save_context,
>>>>>>>> +    .restore_context = clk_periph_gate_restore_context,
>>>>>>>>     };
>>>>>>>>       struct clk *tegra_clk_register_periph_gate(const char *name,
>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph.c
>>>>>>>> b/drivers/clk/tegra/clk-periph.c
>>>>>>>> index 58437da25156..06fb62955768 100644
>>>>>>>> --- a/drivers/clk/tegra/clk-periph.c
>>>>>>>> +++ b/drivers/clk/tegra/clk-periph.c
>>>>>>>> @@ -99,6 +99,37 @@ static void clk_periph_disable(struct clk_hw *hw)
>>>>>>>>         gate_ops->disable(gate_hw);
>>>>>>>>     }
>>>>>>>>     +static int clk_periph_save_context(struct clk_hw *hw)
>>>>>>>> +{
>>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>> +
>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
>>>>>>>> +        gate_ops->save_context(gate_hw);
>>>>>>>> +
>>>>>>>> +    periph->parent_ctx = clk_periph_get_parent(hw);
>>>>>>>> +
>>>>>>>> +    return 0;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static void clk_periph_restore_context(struct clk_hw *hw)
>>>>>>>> +{
>>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>> +    const struct clk_ops *div_ops = periph->div_ops;
>>>>>>>> +    struct clk_hw *div_hw = &periph->divider.hw;
>>>>>>>> +
>>>>>>>> +    clk_periph_set_parent(hw, periph->parent_ctx);
>>>>>>>> +
>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
>>>>>>>> +        div_ops->restore_context(div_hw);
>>>>>>> Could you please point to where the divider's save_context() happens?
>>>>>>> Because I can't see it.
>>>>>> Ah, I now see that there is no need to save the dividers context
>>>>>> because
>>>>>> clk itself has enough info that is needed for the context's restoring
>>>>>> (like I pointed in the review to v6).
>>>>>>
>>>>>> Looks like you could also implement a new clk_hw_get_parent_index()
>>>>>> generic helper to get the index instead of storing it manually.
>>>>> clk_periph_get_parent basically invokes existing clk_mux_ops
>>>>> get_parent() which is then saved in tegra_clk_periph.
>>>>>
>>>>> All existing drivers are using directly get_parent() from clk_mux
>>>>> which actually gets index from the register read.
>>>>>
>>>>> To have this more generic w.r.t save/restore context point of view,
>>>>> probably instead of implementing new get_parent_index helper, I think
>>>>> its better to implement save_context and restore_context to
>>>>> clk_mux_ops along with creating parent_index field into clk_mux to
>>>>> cache index during set_parent.
>>>>>
>>>>> So we just need to invoke mux_ops save_context and restore_context.
>>>>>
>>>> I hope its ok to add save/restore context to clk_mux_ops to be more
>>>> generic w.r.t save/restore context rather than get_parent_index API.
>>>> Please confirm if you agree.
>>> Sounds like a good idea. I see that there is a 'restoring' helper for
>>> the generic clk_gate, seems something similar could be done for the
>>> clk_mux. And looks like anyway you'll need to associate the parent clock
>>> with the hw index in order to restore the muxing.
>> by 'restoring' helper for generic clk_gate, are you referring to
>> clk_gate_restore_context API?
> Yes.
>
>> clk_gate_restore_context is API that's any clk drivers can use for
>> clk_gate operation restore for custom gate clk_ops.
>>
>> But clk-periph is directly using generic clk_mux ops from clk_mux so I
>> think we should add .restore_context to clk_mux_ops and then during
>> clk-periph restore need to invoke mux_ops->restore_context.
> I'm not sure whether it will be good for every driver that uses generic
> clk_mux ops. Should be more flexible to have a generic helper function
> that any driver could use in order to restore the clock's parent.
>
> The clk-periph restoring also includes case of combining divider and
> parent restoring, so generic helper could be useful in that case as well.
>
> It also looks like you could actually use the clk_gate_restore_context()
> instead of manually saving the clock's enable-state, couldn't you?

ok for clk_mux, can add generic clk_mux_restore_context API rather than 
using restore_context in clk_ops and will invoke that during clk_periph 
restore.


Reg clk_gate, looks like we cant use generic clk_gate_restore_context 
for clk-periph as it calls enable/disable callbacks and 
clk_periph_enable/disable in clk-periph-gate also updated refcnt and 
depending on that actual enable/disable is set.

During suspend, peripherals that are already enabled have their refcnt > 
1, so they dont go thru enable/disable on restore if we use same 
enable/disable callback.


Also to align exact reset state along with CLK (like for case where CLK 
is enabled but peripheral might be in reset state), implemented 
save/restore in tegra specific tegra_clk_periph_gate_ops

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-08-01 20:31                 ` Sowjanya Komatineni
@ 2019-08-01 20:54                   ` Dmitry Osipenko
  2019-08-01 21:30                     ` Sowjanya Komatineni
  0 siblings, 1 reply; 67+ messages in thread
From: Dmitry Osipenko @ 2019-08-01 20:54 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree

01.08.2019 23:31, Sowjanya Komatineni пишет:
> 
> On 8/1/19 1:17 PM, Dmitry Osipenko wrote:
>> 01.08.2019 22:42, Sowjanya Komatineni пишет:
>>> On 8/1/19 12:00 PM, Dmitry Osipenko wrote:
>>>> 01.08.2019 20:58, Sowjanya Komatineni пишет:
>>>>> On 7/31/19 4:09 PM, Sowjanya Komatineni wrote:
>>>>>> On 7/31/19 3:44 AM, Dmitry Osipenko wrote:
>>>>>>> 31.07.2019 12:50, Dmitry Osipenko пишет:
>>>>>>>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>>>>>>>> This patch implements save and restore context for peripheral
>>>>>>>>> fixed
>>>>>>>>> clock ops, peripheral gate clock ops, sdmmc mux clock ops, and
>>>>>>>>> peripheral clock ops.
>>>>>>>>>
>>>>>>>>> During system suspend, core power goes off and looses the settings
>>>>>>>>> of the Tegra CAR controller registers.
>>>>>>>>>
>>>>>>>>> So during suspend entry clock and reset state of peripherals is
>>>>>>>>> saved
>>>>>>>>> and on resume they are restored to have clocks back to same
>>>>>>>>> rate and
>>>>>>>>> state as before suspend.
>>>>>>>>>
>>>>>>>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>>>>>>>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>>>>>>>>> ---
>>>>>>>>>     drivers/clk/tegra/clk-periph-fixed.c | 33
>>>>>>>>> ++++++++++++++++++++++++++++++++
>>>>>>>>>     drivers/clk/tegra/clk-periph-gate.c  | 34
>>>>>>>>> +++++++++++++++++++++++++++++++++
>>>>>>>>>     drivers/clk/tegra/clk-periph.c       | 37
>>>>>>>>> ++++++++++++++++++++++++++++++++++++
>>>>>>>>>     drivers/clk/tegra/clk-sdmmc-mux.c    | 28
>>>>>>>>> +++++++++++++++++++++++++++
>>>>>>>>>     drivers/clk/tegra/clk.h              |  6 ++++++
>>>>>>>>>     5 files changed, 138 insertions(+)
>>>>>>>>>
>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>> b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>> index c088e7a280df..21b24530fa00 100644
>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>> @@ -60,11 +60,44 @@ tegra_clk_periph_fixed_recalc_rate(struct
>>>>>>>>> clk_hw *hw,
>>>>>>>>>         return (unsigned long)rate;
>>>>>>>>>     }
>>>>>>>>>     +static int tegra_clk_periph_fixed_save_context(struct clk_hw
>>>>>>>>> *hw)
>>>>>>>>> +{
>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>> +
>>>>>>>>> +    fixed->enb_ctx = readl_relaxed(fixed->base +
>>>>>>>>> fixed->regs->enb_reg) &
>>>>>>>>> +             mask;
>>>>>>>>> +    fixed->rst_ctx = readl_relaxed(fixed->base +
>>>>>>>>> fixed->regs->rst_reg) &
>>>>>>>>> +             mask;
>>>>>>>>> +
>>>>>>>>> +    return 0;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static void tegra_clk_periph_fixed_restore_context(struct clk_hw
>>>>>>>>> *hw)
>>>>>>>>> +{
>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>> +
>>>>>>>>> +    if (fixed->enb_ctx)
>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>> fixed->regs->enb_set_reg);
>>>>>>>>> +    else
>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>> fixed->regs->enb_clr_reg);
>>>>>>>>> +
>>>>>>>>> +    udelay(2);
>>>>>>>>> +
>>>>>>>>> +    if (!fixed->rst_ctx) {
>>>>>>>>> +        udelay(5); /* reset propogation delay */
>>>>>>>>> +        writel_relaxed(mask, fixed->base + fixed->regs->rst_reg);
>>>>>>>>> +    }
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>>     static const struct clk_ops tegra_clk_periph_fixed_ops = {
>>>>>>>>>         .is_enabled = tegra_clk_periph_fixed_is_enabled,
>>>>>>>>>         .enable = tegra_clk_periph_fixed_enable,
>>>>>>>>>         .disable = tegra_clk_periph_fixed_disable,
>>>>>>>>>         .recalc_rate = tegra_clk_periph_fixed_recalc_rate,
>>>>>>>>> +    .save_context = tegra_clk_periph_fixed_save_context,
>>>>>>>>> +    .restore_context = tegra_clk_periph_fixed_restore_context,
>>>>>>>>>     };
>>>>>>>>>       struct clk *tegra_clk_register_periph_fixed(const char
>>>>>>>>> *name,
>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>> b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>> index 4b31beefc9fc..6ba5b08e0787 100644
>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>> @@ -25,6 +25,8 @@ static DEFINE_SPINLOCK(periph_ref_lock);
>>>>>>>>>       #define read_rst(gate) \
>>>>>>>>>         readl_relaxed(gate->clk_base + (gate->regs->rst_reg))
>>>>>>>>> +#define write_rst_set(val, gate) \
>>>>>>>>> +    writel_relaxed(val, gate->clk_base +
>>>>>>>>> (gate->regs->rst_set_reg))
>>>>>>>>>     #define write_rst_clr(val, gate) \
>>>>>>>>>         writel_relaxed(val, gate->clk_base +
>>>>>>>>> (gate->regs->rst_clr_reg))
>>>>>>>>>     @@ -110,10 +112,42 @@ static void clk_periph_disable(struct
>>>>>>>>> clk_hw *hw)
>>>>>>>>>         spin_unlock_irqrestore(&periph_ref_lock, flags);
>>>>>>>>>     }
>>>>>>>>>     +static int clk_periph_gate_save_context(struct clk_hw *hw)
>>>>>>>>> +{
>>>>>>>>> +    struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
>>>>>>>>> +
>>>>>>>>> +    gate->clk_state_ctx = read_enb(gate) &
>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>> +    gate->rst_state_ctx = read_rst(gate) &
>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>> +
>>>>>>>>> +    return 0;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static void clk_periph_gate_restore_context(struct clk_hw *hw)
>>>>>>>>> +{
>>>>>>>>> +    struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
>>>>>>>>> +
>>>>>>>>> +    if (gate->clk_state_ctx)
>>>>>>>>> +        write_enb_set(periph_clk_to_bit(gate), gate);
>>>>>>>>> +    else
>>>>>>>>> +        write_enb_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>> +
>>>>>>>>> +    udelay(5);
>>>>>>>>> +
>>>>>>>>> +    if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
>>>>>>>>> +        !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
>>>>>>>>> +        if (gate->rst_state_ctx)
>>>>>>>>> +            write_rst_set(periph_clk_to_bit(gate), gate);
>>>>>>>>> +        else
>>>>>>>>> +            write_rst_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>> +    }
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>>     const struct clk_ops tegra_clk_periph_gate_ops = {
>>>>>>>>>         .is_enabled = clk_periph_is_enabled,
>>>>>>>>>         .enable = clk_periph_enable,
>>>>>>>>>         .disable = clk_periph_disable,
>>>>>>>>> +    .save_context = clk_periph_gate_save_context,
>>>>>>>>> +    .restore_context = clk_periph_gate_restore_context,
>>>>>>>>>     };
>>>>>>>>>       struct clk *tegra_clk_register_periph_gate(const char *name,
>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph.c
>>>>>>>>> b/drivers/clk/tegra/clk-periph.c
>>>>>>>>> index 58437da25156..06fb62955768 100644
>>>>>>>>> --- a/drivers/clk/tegra/clk-periph.c
>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph.c
>>>>>>>>> @@ -99,6 +99,37 @@ static void clk_periph_disable(struct clk_hw
>>>>>>>>> *hw)
>>>>>>>>>         gate_ops->disable(gate_hw);
>>>>>>>>>     }
>>>>>>>>>     +static int clk_periph_save_context(struct clk_hw *hw)
>>>>>>>>> +{
>>>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>> +
>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
>>>>>>>>> +        gate_ops->save_context(gate_hw);
>>>>>>>>> +
>>>>>>>>> +    periph->parent_ctx = clk_periph_get_parent(hw);
>>>>>>>>> +
>>>>>>>>> +    return 0;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static void clk_periph_restore_context(struct clk_hw *hw)
>>>>>>>>> +{
>>>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>> +    const struct clk_ops *div_ops = periph->div_ops;
>>>>>>>>> +    struct clk_hw *div_hw = &periph->divider.hw;
>>>>>>>>> +
>>>>>>>>> +    clk_periph_set_parent(hw, periph->parent_ctx);
>>>>>>>>> +
>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
>>>>>>>>> +        div_ops->restore_context(div_hw);
>>>>>>>> Could you please point to where the divider's save_context()
>>>>>>>> happens?
>>>>>>>> Because I can't see it.
>>>>>>> Ah, I now see that there is no need to save the dividers context
>>>>>>> because
>>>>>>> clk itself has enough info that is needed for the context's
>>>>>>> restoring
>>>>>>> (like I pointed in the review to v6).
>>>>>>>
>>>>>>> Looks like you could also implement a new clk_hw_get_parent_index()
>>>>>>> generic helper to get the index instead of storing it manually.
>>>>>> clk_periph_get_parent basically invokes existing clk_mux_ops
>>>>>> get_parent() which is then saved in tegra_clk_periph.
>>>>>>
>>>>>> All existing drivers are using directly get_parent() from clk_mux
>>>>>> which actually gets index from the register read.
>>>>>>
>>>>>> To have this more generic w.r.t save/restore context point of view,
>>>>>> probably instead of implementing new get_parent_index helper, I think
>>>>>> its better to implement save_context and restore_context to
>>>>>> clk_mux_ops along with creating parent_index field into clk_mux to
>>>>>> cache index during set_parent.
>>>>>>
>>>>>> So we just need to invoke mux_ops save_context and restore_context.
>>>>>>
>>>>> I hope its ok to add save/restore context to clk_mux_ops to be more
>>>>> generic w.r.t save/restore context rather than get_parent_index API.
>>>>> Please confirm if you agree.
>>>> Sounds like a good idea. I see that there is a 'restoring' helper for
>>>> the generic clk_gate, seems something similar could be done for the
>>>> clk_mux. And looks like anyway you'll need to associate the parent
>>>> clock
>>>> with the hw index in order to restore the muxing.
>>> by 'restoring' helper for generic clk_gate, are you referring to
>>> clk_gate_restore_context API?
>> Yes.
>>
>>> clk_gate_restore_context is API that's any clk drivers can use for
>>> clk_gate operation restore for custom gate clk_ops.
>>>
>>> But clk-periph is directly using generic clk_mux ops from clk_mux so I
>>> think we should add .restore_context to clk_mux_ops and then during
>>> clk-periph restore need to invoke mux_ops->restore_context.
>> I'm not sure whether it will be good for every driver that uses generic
>> clk_mux ops. Should be more flexible to have a generic helper function
>> that any driver could use in order to restore the clock's parent.
>>
>> The clk-periph restoring also includes case of combining divider and
>> parent restoring, so generic helper could be useful in that case as well.
>>
>> It also looks like you could actually use the clk_gate_restore_context()
>> instead of manually saving the clock's enable-state, couldn't you?
> 
> ok for clk_mux, can add generic clk_mux_restore_context API rather than
> using restore_context in clk_ops and will invoke that during clk_periph
> restore.
> 
> 
> Reg clk_gate, looks like we cant use generic clk_gate_restore_context
> for clk-periph as it calls enable/disable callbacks and
> clk_periph_enable/disable in clk-periph-gate also updated refcnt and
> depending on that actual enable/disable is set.
> 
> During suspend, peripherals that are already enabled have their refcnt >
> 1, so they dont go thru enable/disable on restore if we use same
> enable/disable callback.

Looks like you could just decrement the gate's enable_refcnt on
save_context, wouldn't that work?

> 
> Also to align exact reset state along with CLK (like for case where CLK
> is enabled but peripheral might be in reset state), implemented
> save/restore in tegra specific tegra_clk_periph_gate_ops

I'm wondering whether instead of saving/restoring reset-state of every
clock, you could simply save/restore the whole RST_DEV_x_SET register.
Couldn't you?

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-08-01 20:54                   ` Dmitry Osipenko
@ 2019-08-01 21:30                     ` Sowjanya Komatineni
  2019-08-01 23:19                       ` Sowjanya Komatineni
  0 siblings, 1 reply; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-08-01 21:30 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree


On 8/1/19 1:54 PM, Dmitry Osipenko wrote:
> 01.08.2019 23:31, Sowjanya Komatineni пишет:
>> On 8/1/19 1:17 PM, Dmitry Osipenko wrote:
>>> 01.08.2019 22:42, Sowjanya Komatineni пишет:
>>>> On 8/1/19 12:00 PM, Dmitry Osipenko wrote:
>>>>> 01.08.2019 20:58, Sowjanya Komatineni пишет:
>>>>>> On 7/31/19 4:09 PM, Sowjanya Komatineni wrote:
>>>>>>> On 7/31/19 3:44 AM, Dmitry Osipenko wrote:
>>>>>>>> 31.07.2019 12:50, Dmitry Osipenko пишет:
>>>>>>>>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>>>>>>>>> This patch implements save and restore context for peripheral
>>>>>>>>>> fixed
>>>>>>>>>> clock ops, peripheral gate clock ops, sdmmc mux clock ops, and
>>>>>>>>>> peripheral clock ops.
>>>>>>>>>>
>>>>>>>>>> During system suspend, core power goes off and looses the settings
>>>>>>>>>> of the Tegra CAR controller registers.
>>>>>>>>>>
>>>>>>>>>> So during suspend entry clock and reset state of peripherals is
>>>>>>>>>> saved
>>>>>>>>>> and on resume they are restored to have clocks back to same
>>>>>>>>>> rate and
>>>>>>>>>> state as before suspend.
>>>>>>>>>>
>>>>>>>>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>>>>>>>>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>>>>>>>>>> ---
>>>>>>>>>>      drivers/clk/tegra/clk-periph-fixed.c | 33
>>>>>>>>>> ++++++++++++++++++++++++++++++++
>>>>>>>>>>      drivers/clk/tegra/clk-periph-gate.c  | 34
>>>>>>>>>> +++++++++++++++++++++++++++++++++
>>>>>>>>>>      drivers/clk/tegra/clk-periph.c       | 37
>>>>>>>>>> ++++++++++++++++++++++++++++++++++++
>>>>>>>>>>      drivers/clk/tegra/clk-sdmmc-mux.c    | 28
>>>>>>>>>> +++++++++++++++++++++++++++
>>>>>>>>>>      drivers/clk/tegra/clk.h              |  6 ++++++
>>>>>>>>>>      5 files changed, 138 insertions(+)
>>>>>>>>>>
>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>> b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>> index c088e7a280df..21b24530fa00 100644
>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>> @@ -60,11 +60,44 @@ tegra_clk_periph_fixed_recalc_rate(struct
>>>>>>>>>> clk_hw *hw,
>>>>>>>>>>          return (unsigned long)rate;
>>>>>>>>>>      }
>>>>>>>>>>      +static int tegra_clk_periph_fixed_save_context(struct clk_hw
>>>>>>>>>> *hw)
>>>>>>>>>> +{
>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>> +
>>>>>>>>>> +    fixed->enb_ctx = readl_relaxed(fixed->base +
>>>>>>>>>> fixed->regs->enb_reg) &
>>>>>>>>>> +             mask;
>>>>>>>>>> +    fixed->rst_ctx = readl_relaxed(fixed->base +
>>>>>>>>>> fixed->regs->rst_reg) &
>>>>>>>>>> +             mask;
>>>>>>>>>> +
>>>>>>>>>> +    return 0;
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +static void tegra_clk_periph_fixed_restore_context(struct clk_hw
>>>>>>>>>> *hw)
>>>>>>>>>> +{
>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>> +
>>>>>>>>>> +    if (fixed->enb_ctx)
>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>> fixed->regs->enb_set_reg);
>>>>>>>>>> +    else
>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>> fixed->regs->enb_clr_reg);
>>>>>>>>>> +
>>>>>>>>>> +    udelay(2);
>>>>>>>>>> +
>>>>>>>>>> +    if (!fixed->rst_ctx) {
>>>>>>>>>> +        udelay(5); /* reset propogation delay */
>>>>>>>>>> +        writel_relaxed(mask, fixed->base + fixed->regs->rst_reg);
>>>>>>>>>> +    }
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>>      static const struct clk_ops tegra_clk_periph_fixed_ops = {
>>>>>>>>>>          .is_enabled = tegra_clk_periph_fixed_is_enabled,
>>>>>>>>>>          .enable = tegra_clk_periph_fixed_enable,
>>>>>>>>>>          .disable = tegra_clk_periph_fixed_disable,
>>>>>>>>>>          .recalc_rate = tegra_clk_periph_fixed_recalc_rate,
>>>>>>>>>> +    .save_context = tegra_clk_periph_fixed_save_context,
>>>>>>>>>> +    .restore_context = tegra_clk_periph_fixed_restore_context,
>>>>>>>>>>      };
>>>>>>>>>>        struct clk *tegra_clk_register_periph_fixed(const char
>>>>>>>>>> *name,
>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>> b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>> index 4b31beefc9fc..6ba5b08e0787 100644
>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>> @@ -25,6 +25,8 @@ static DEFINE_SPINLOCK(periph_ref_lock);
>>>>>>>>>>        #define read_rst(gate) \
>>>>>>>>>>          readl_relaxed(gate->clk_base + (gate->regs->rst_reg))
>>>>>>>>>> +#define write_rst_set(val, gate) \
>>>>>>>>>> +    writel_relaxed(val, gate->clk_base +
>>>>>>>>>> (gate->regs->rst_set_reg))
>>>>>>>>>>      #define write_rst_clr(val, gate) \
>>>>>>>>>>          writel_relaxed(val, gate->clk_base +
>>>>>>>>>> (gate->regs->rst_clr_reg))
>>>>>>>>>>      @@ -110,10 +112,42 @@ static void clk_periph_disable(struct
>>>>>>>>>> clk_hw *hw)
>>>>>>>>>>          spin_unlock_irqrestore(&periph_ref_lock, flags);
>>>>>>>>>>      }
>>>>>>>>>>      +static int clk_periph_gate_save_context(struct clk_hw *hw)
>>>>>>>>>> +{
>>>>>>>>>> +    struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
>>>>>>>>>> +
>>>>>>>>>> +    gate->clk_state_ctx = read_enb(gate) &
>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>> +    gate->rst_state_ctx = read_rst(gate) &
>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>> +
>>>>>>>>>> +    return 0;
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +static void clk_periph_gate_restore_context(struct clk_hw *hw)
>>>>>>>>>> +{
>>>>>>>>>> +    struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
>>>>>>>>>> +
>>>>>>>>>> +    if (gate->clk_state_ctx)
>>>>>>>>>> +        write_enb_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>> +    else
>>>>>>>>>> +        write_enb_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>> +
>>>>>>>>>> +    udelay(5);
>>>>>>>>>> +
>>>>>>>>>> +    if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
>>>>>>>>>> +        !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
>>>>>>>>>> +        if (gate->rst_state_ctx)
>>>>>>>>>> +            write_rst_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>> +        else
>>>>>>>>>> +            write_rst_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>> +    }
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>>      const struct clk_ops tegra_clk_periph_gate_ops = {
>>>>>>>>>>          .is_enabled = clk_periph_is_enabled,
>>>>>>>>>>          .enable = clk_periph_enable,
>>>>>>>>>>          .disable = clk_periph_disable,
>>>>>>>>>> +    .save_context = clk_periph_gate_save_context,
>>>>>>>>>> +    .restore_context = clk_periph_gate_restore_context,
>>>>>>>>>>      };
>>>>>>>>>>        struct clk *tegra_clk_register_periph_gate(const char *name,
>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>> b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>> index 58437da25156..06fb62955768 100644
>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>> @@ -99,6 +99,37 @@ static void clk_periph_disable(struct clk_hw
>>>>>>>>>> *hw)
>>>>>>>>>>          gate_ops->disable(gate_hw);
>>>>>>>>>>      }
>>>>>>>>>>      +static int clk_periph_save_context(struct clk_hw *hw)
>>>>>>>>>> +{
>>>>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>> +
>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
>>>>>>>>>> +        gate_ops->save_context(gate_hw);
>>>>>>>>>> +
>>>>>>>>>> +    periph->parent_ctx = clk_periph_get_parent(hw);
>>>>>>>>>> +
>>>>>>>>>> +    return 0;
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +static void clk_periph_restore_context(struct clk_hw *hw)
>>>>>>>>>> +{
>>>>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>> +    const struct clk_ops *div_ops = periph->div_ops;
>>>>>>>>>> +    struct clk_hw *div_hw = &periph->divider.hw;
>>>>>>>>>> +
>>>>>>>>>> +    clk_periph_set_parent(hw, periph->parent_ctx);
>>>>>>>>>> +
>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
>>>>>>>>>> +        div_ops->restore_context(div_hw);
>>>>>>>>> Could you please point to where the divider's save_context()
>>>>>>>>> happens?
>>>>>>>>> Because I can't see it.
>>>>>>>> Ah, I now see that there is no need to save the dividers context
>>>>>>>> because
>>>>>>>> clk itself has enough info that is needed for the context's
>>>>>>>> restoring
>>>>>>>> (like I pointed in the review to v6).
>>>>>>>>
>>>>>>>> Looks like you could also implement a new clk_hw_get_parent_index()
>>>>>>>> generic helper to get the index instead of storing it manually.
>>>>>>> clk_periph_get_parent basically invokes existing clk_mux_ops
>>>>>>> get_parent() which is then saved in tegra_clk_periph.
>>>>>>>
>>>>>>> All existing drivers are using directly get_parent() from clk_mux
>>>>>>> which actually gets index from the register read.
>>>>>>>
>>>>>>> To have this more generic w.r.t save/restore context point of view,
>>>>>>> probably instead of implementing new get_parent_index helper, I think
>>>>>>> its better to implement save_context and restore_context to
>>>>>>> clk_mux_ops along with creating parent_index field into clk_mux to
>>>>>>> cache index during set_parent.
>>>>>>>
>>>>>>> So we just need to invoke mux_ops save_context and restore_context.
>>>>>>>
>>>>>> I hope its ok to add save/restore context to clk_mux_ops to be more
>>>>>> generic w.r.t save/restore context rather than get_parent_index API.
>>>>>> Please confirm if you agree.
>>>>> Sounds like a good idea. I see that there is a 'restoring' helper for
>>>>> the generic clk_gate, seems something similar could be done for the
>>>>> clk_mux. And looks like anyway you'll need to associate the parent
>>>>> clock
>>>>> with the hw index in order to restore the muxing.
>>>> by 'restoring' helper for generic clk_gate, are you referring to
>>>> clk_gate_restore_context API?
>>> Yes.
>>>
>>>> clk_gate_restore_context is API that's any clk drivers can use for
>>>> clk_gate operation restore for custom gate clk_ops.
>>>>
>>>> But clk-periph is directly using generic clk_mux ops from clk_mux so I
>>>> think we should add .restore_context to clk_mux_ops and then during
>>>> clk-periph restore need to invoke mux_ops->restore_context.
>>> I'm not sure whether it will be good for every driver that uses generic
>>> clk_mux ops. Should be more flexible to have a generic helper function
>>> that any driver could use in order to restore the clock's parent.
>>>
>>> The clk-periph restoring also includes case of combining divider and
>>> parent restoring, so generic helper could be useful in that case as well.
>>>
>>> It also looks like you could actually use the clk_gate_restore_context()
>>> instead of manually saving the clock's enable-state, couldn't you?
>> ok for clk_mux, can add generic clk_mux_restore_context API rather than
>> using restore_context in clk_ops and will invoke that during clk_periph
>> restore.
>>
>>
>> Reg clk_gate, looks like we cant use generic clk_gate_restore_context
>> for clk-periph as it calls enable/disable callbacks and
>> clk_periph_enable/disable in clk-periph-gate also updated refcnt and
>> depending on that actual enable/disable is set.
>>
>> During suspend, peripherals that are already enabled have their refcnt >
>> 1, so they dont go thru enable/disable on restore if we use same
>> enable/disable callback.
> Looks like you could just decrement the gate's enable_refcnt on
> save_context, wouldn't that work?
>
>> Also to align exact reset state along with CLK (like for case where CLK
>> is enabled but peripheral might be in reset state), implemented
>> save/restore in tegra specific tegra_clk_periph_gate_ops
> I'm wondering whether instead of saving/restoring reset-state of every
> clock, you could simply save/restore the whole RST_DEV_x_SET register.
> Couldn't you?
Thats what I was doing in first version of patch. But later as we moved 
to use clk_save_context and clk_restore_context, peripheral clk_hw RST & 
CLK enables happen thru its corresponding save/restore after source restore

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-08-01 21:30                     ` Sowjanya Komatineni
@ 2019-08-01 23:19                       ` Sowjanya Komatineni
  2019-08-01 23:49                         ` Sowjanya Komatineni
  0 siblings, 1 reply; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-08-01 23:19 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree


On 8/1/19 2:30 PM, Sowjanya Komatineni wrote:
>
> On 8/1/19 1:54 PM, Dmitry Osipenko wrote:
>> 01.08.2019 23:31, Sowjanya Komatineni пишет:
>>> On 8/1/19 1:17 PM, Dmitry Osipenko wrote:
>>>> 01.08.2019 22:42, Sowjanya Komatineni пишет:
>>>>> On 8/1/19 12:00 PM, Dmitry Osipenko wrote:
>>>>>> 01.08.2019 20:58, Sowjanya Komatineni пишет:
>>>>>>> On 7/31/19 4:09 PM, Sowjanya Komatineni wrote:
>>>>>>>> On 7/31/19 3:44 AM, Dmitry Osipenko wrote:
>>>>>>>>> 31.07.2019 12:50, Dmitry Osipenko пишет:
>>>>>>>>>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>>>>>>>>>> This patch implements save and restore context for peripheral
>>>>>>>>>>> fixed
>>>>>>>>>>> clock ops, peripheral gate clock ops, sdmmc mux clock ops, and
>>>>>>>>>>> peripheral clock ops.
>>>>>>>>>>>
>>>>>>>>>>> During system suspend, core power goes off and looses the 
>>>>>>>>>>> settings
>>>>>>>>>>> of the Tegra CAR controller registers.
>>>>>>>>>>>
>>>>>>>>>>> So during suspend entry clock and reset state of peripherals is
>>>>>>>>>>> saved
>>>>>>>>>>> and on resume they are restored to have clocks back to same
>>>>>>>>>>> rate and
>>>>>>>>>>> state as before suspend.
>>>>>>>>>>>
>>>>>>>>>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>>>>>>>>>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>>>>>>>>>>> ---
>>>>>>>>>>>      drivers/clk/tegra/clk-periph-fixed.c | 33
>>>>>>>>>>> ++++++++++++++++++++++++++++++++
>>>>>>>>>>>      drivers/clk/tegra/clk-periph-gate.c  | 34
>>>>>>>>>>> +++++++++++++++++++++++++++++++++
>>>>>>>>>>>      drivers/clk/tegra/clk-periph.c       | 37
>>>>>>>>>>> ++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>      drivers/clk/tegra/clk-sdmmc-mux.c    | 28
>>>>>>>>>>> +++++++++++++++++++++++++++
>>>>>>>>>>>      drivers/clk/tegra/clk.h              |  6 ++++++
>>>>>>>>>>>      5 files changed, 138 insertions(+)
>>>>>>>>>>>
>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>> index c088e7a280df..21b24530fa00 100644
>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>> @@ -60,11 +60,44 @@ tegra_clk_periph_fixed_recalc_rate(struct
>>>>>>>>>>> clk_hw *hw,
>>>>>>>>>>>          return (unsigned long)rate;
>>>>>>>>>>>      }
>>>>>>>>>>>      +static int tegra_clk_periph_fixed_save_context(struct 
>>>>>>>>>>> clk_hw
>>>>>>>>>>> *hw)
>>>>>>>>>>> +{
>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>> +
>>>>>>>>>>> +    fixed->enb_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>> fixed->regs->enb_reg) &
>>>>>>>>>>> +             mask;
>>>>>>>>>>> +    fixed->rst_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>> fixed->regs->rst_reg) &
>>>>>>>>>>> +             mask;
>>>>>>>>>>> +
>>>>>>>>>>> +    return 0;
>>>>>>>>>>> +}
>>>>>>>>>>> +
>>>>>>>>>>> +static void tegra_clk_periph_fixed_restore_context(struct 
>>>>>>>>>>> clk_hw
>>>>>>>>>>> *hw)
>>>>>>>>>>> +{
>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>> +
>>>>>>>>>>> +    if (fixed->enb_ctx)
>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>> fixed->regs->enb_set_reg);
>>>>>>>>>>> +    else
>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>> fixed->regs->enb_clr_reg);
>>>>>>>>>>> +
>>>>>>>>>>> +    udelay(2);
>>>>>>>>>>> +
>>>>>>>>>>> +    if (!fixed->rst_ctx) {
>>>>>>>>>>> +        udelay(5); /* reset propogation delay */
>>>>>>>>>>> +        writel_relaxed(mask, fixed->base + 
>>>>>>>>>>> fixed->regs->rst_reg);
>>>>>>>>>>> +    }
>>>>>>>>>>> +}
>>>>>>>>>>> +
>>>>>>>>>>>      static const struct clk_ops tegra_clk_periph_fixed_ops = {
>>>>>>>>>>>          .is_enabled = tegra_clk_periph_fixed_is_enabled,
>>>>>>>>>>>          .enable = tegra_clk_periph_fixed_enable,
>>>>>>>>>>>          .disable = tegra_clk_periph_fixed_disable,
>>>>>>>>>>>          .recalc_rate = tegra_clk_periph_fixed_recalc_rate,
>>>>>>>>>>> +    .save_context = tegra_clk_periph_fixed_save_context,
>>>>>>>>>>> +    .restore_context = tegra_clk_periph_fixed_restore_context,
>>>>>>>>>>>      };
>>>>>>>>>>>        struct clk *tegra_clk_register_periph_fixed(const char
>>>>>>>>>>> *name,
>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>> index 4b31beefc9fc..6ba5b08e0787 100644
>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>> @@ -25,6 +25,8 @@ static DEFINE_SPINLOCK(periph_ref_lock);
>>>>>>>>>>>        #define read_rst(gate) \
>>>>>>>>>>>          readl_relaxed(gate->clk_base + (gate->regs->rst_reg))
>>>>>>>>>>> +#define write_rst_set(val, gate) \
>>>>>>>>>>> +    writel_relaxed(val, gate->clk_base +
>>>>>>>>>>> (gate->regs->rst_set_reg))
>>>>>>>>>>>      #define write_rst_clr(val, gate) \
>>>>>>>>>>>          writel_relaxed(val, gate->clk_base +
>>>>>>>>>>> (gate->regs->rst_clr_reg))
>>>>>>>>>>>      @@ -110,10 +112,42 @@ static void 
>>>>>>>>>>> clk_periph_disable(struct
>>>>>>>>>>> clk_hw *hw)
>>>>>>>>>>> spin_unlock_irqrestore(&periph_ref_lock, flags);
>>>>>>>>>>>      }
>>>>>>>>>>>      +static int clk_periph_gate_save_context(struct clk_hw 
>>>>>>>>>>> *hw)
>>>>>>>>>>> +{
>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate = 
>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>> +
>>>>>>>>>>> +    gate->clk_state_ctx = read_enb(gate) &
>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>> +    gate->rst_state_ctx = read_rst(gate) &
>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>> +
>>>>>>>>>>> +    return 0;
>>>>>>>>>>> +}
>>>>>>>>>>> +
>>>>>>>>>>> +static void clk_periph_gate_restore_context(struct clk_hw *hw)
>>>>>>>>>>> +{
>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate = 
>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>> +
>>>>>>>>>>> +    if (gate->clk_state_ctx)
>>>>>>>>>>> + write_enb_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>> +    else
>>>>>>>>>>> + write_enb_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>> +
>>>>>>>>>>> +    udelay(5);
>>>>>>>>>>> +
>>>>>>>>>>> +    if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
>>>>>>>>>>> +        !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
>>>>>>>>>>> +        if (gate->rst_state_ctx)
>>>>>>>>>>> + write_rst_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>> +        else
>>>>>>>>>>> + write_rst_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>> +    }
>>>>>>>>>>> +}
>>>>>>>>>>> +
>>>>>>>>>>>      const struct clk_ops tegra_clk_periph_gate_ops = {
>>>>>>>>>>>          .is_enabled = clk_periph_is_enabled,
>>>>>>>>>>>          .enable = clk_periph_enable,
>>>>>>>>>>>          .disable = clk_periph_disable,
>>>>>>>>>>> +    .save_context = clk_periph_gate_save_context,
>>>>>>>>>>> +    .restore_context = clk_periph_gate_restore_context,
>>>>>>>>>>>      };
>>>>>>>>>>>        struct clk *tegra_clk_register_periph_gate(const char 
>>>>>>>>>>> *name,
>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>> b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>> index 58437da25156..06fb62955768 100644
>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>> @@ -99,6 +99,37 @@ static void clk_periph_disable(struct clk_hw
>>>>>>>>>>> *hw)
>>>>>>>>>>>          gate_ops->disable(gate_hw);
>>>>>>>>>>>      }
>>>>>>>>>>>      +static int clk_periph_save_context(struct clk_hw *hw)
>>>>>>>>>>> +{
>>>>>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>> +
>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
>>>>>>>>>>> +        gate_ops->save_context(gate_hw);
>>>>>>>>>>> +
>>>>>>>>>>> +    periph->parent_ctx = clk_periph_get_parent(hw);
>>>>>>>>>>> +
>>>>>>>>>>> +    return 0;
>>>>>>>>>>> +}
>>>>>>>>>>> +
>>>>>>>>>>> +static void clk_periph_restore_context(struct clk_hw *hw)
>>>>>>>>>>> +{
>>>>>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>> +    const struct clk_ops *div_ops = periph->div_ops;
>>>>>>>>>>> +    struct clk_hw *div_hw = &periph->divider.hw;
>>>>>>>>>>> +
>>>>>>>>>>> +    clk_periph_set_parent(hw, periph->parent_ctx);
>>>>>>>>>>> +
>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
>>>>>>>>>>> +        div_ops->restore_context(div_hw);
>>>>>>>>>> Could you please point to where the divider's save_context()
>>>>>>>>>> happens?
>>>>>>>>>> Because I can't see it.
>>>>>>>>> Ah, I now see that there is no need to save the dividers context
>>>>>>>>> because
>>>>>>>>> clk itself has enough info that is needed for the context's
>>>>>>>>> restoring
>>>>>>>>> (like I pointed in the review to v6).
>>>>>>>>>
>>>>>>>>> Looks like you could also implement a new 
>>>>>>>>> clk_hw_get_parent_index()
>>>>>>>>> generic helper to get the index instead of storing it manually.
>>>>>>>> clk_periph_get_parent basically invokes existing clk_mux_ops
>>>>>>>> get_parent() which is then saved in tegra_clk_periph.
>>>>>>>>
>>>>>>>> All existing drivers are using directly get_parent() from clk_mux
>>>>>>>> which actually gets index from the register read.
>>>>>>>>
>>>>>>>> To have this more generic w.r.t save/restore context point of 
>>>>>>>> view,
>>>>>>>> probably instead of implementing new get_parent_index helper, I 
>>>>>>>> think
>>>>>>>> its better to implement save_context and restore_context to
>>>>>>>> clk_mux_ops along with creating parent_index field into clk_mux to
>>>>>>>> cache index during set_parent.
>>>>>>>>
>>>>>>>> So we just need to invoke mux_ops save_context and 
>>>>>>>> restore_context.
>>>>>>>>
>>>>>>> I hope its ok to add save/restore context to clk_mux_ops to be more
>>>>>>> generic w.r.t save/restore context rather than get_parent_index 
>>>>>>> API.
>>>>>>> Please confirm if you agree.
>>>>>> Sounds like a good idea. I see that there is a 'restoring' helper 
>>>>>> for
>>>>>> the generic clk_gate, seems something similar could be done for the
>>>>>> clk_mux. And looks like anyway you'll need to associate the parent
>>>>>> clock
>>>>>> with the hw index in order to restore the muxing.
>>>>> by 'restoring' helper for generic clk_gate, are you referring to
>>>>> clk_gate_restore_context API?
>>>> Yes.
>>>>
>>>>> clk_gate_restore_context is API that's any clk drivers can use for
>>>>> clk_gate operation restore for custom gate clk_ops.
>>>>>
>>>>> But clk-periph is directly using generic clk_mux ops from clk_mux 
>>>>> so I
>>>>> think we should add .restore_context to clk_mux_ops and then during
>>>>> clk-periph restore need to invoke mux_ops->restore_context.
>>>> I'm not sure whether it will be good for every driver that uses 
>>>> generic
>>>> clk_mux ops. Should be more flexible to have a generic helper function
>>>> that any driver could use in order to restore the clock's parent.
>>>>
>>>> The clk-periph restoring also includes case of combining divider and
>>>> parent restoring, so generic helper could be useful in that case as 
>>>> well.
>>>>
>>>> It also looks like you could actually use the 
>>>> clk_gate_restore_context()
>>>> instead of manually saving the clock's enable-state, couldn't you?
>>> ok for clk_mux, can add generic clk_mux_restore_context API rather than
>>> using restore_context in clk_ops and will invoke that during clk_periph
>>> restore.
>>>
>>>
>>> Reg clk_gate, looks like we cant use generic clk_gate_restore_context
>>> for clk-periph as it calls enable/disable callbacks and
>>> clk_periph_enable/disable in clk-periph-gate also updated refcnt and
>>> depending on that actual enable/disable is set.
>>>
>>> During suspend, peripherals that are already enabled have their 
>>> refcnt >
>>> 1, so they dont go thru enable/disable on restore if we use same
>>> enable/disable callback.
>> Looks like you could just decrement the gate's enable_refcnt on
>> save_context, wouldn't that work?
>>
gate->enable_refcnt is within clk-periph-gate which gets updated when 
enable/disable callbacks get execute thru clk_core_enable/disable.
But actual enable_count used in clk_gate_restore_context is the one 
which gets updated with in the clk core enable/disable functions which 
invokes these callbacks. Depending on this enable_count in clk core it 
invokes enable/disable.

So, this will cause mismatch if we handle refcnt during save/restore of 
tegra_clk_periph_gate_ops and also enable/disable thru this 
clk_gate_restore_context is based on enable_count from clk core.

>>> Also to align exact reset state along with CLK (like for case where CLK
>>> is enabled but peripheral might be in reset state), implemented
>>> save/restore in tegra specific tegra_clk_periph_gate_ops
>> I'm wondering whether instead of saving/restoring reset-state of every
>> clock, you could simply save/restore the whole RST_DEV_x_SET register.
>> Couldn't you?
> Thats what I was doing in first version of patch. But later as we 
> moved to use clk_save_context and clk_restore_context, peripheral 
> clk_hw RST & CLK enables happen thru its corresponding save/restore 
> after source restore


Also, to align both CLK & RST to the exact state of register, doing 
save/restore in tegra_clk_periph_gate_ops and invoking this after source 
restore for peripheral clock, seems cleaner to avoid any 
misconfiguration b/w rst & clk settings.

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-08-01 23:19                       ` Sowjanya Komatineni
@ 2019-08-01 23:49                         ` Sowjanya Komatineni
  2019-08-02 12:38                           ` Dmitry Osipenko
  0 siblings, 1 reply; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-08-01 23:49 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree


On 8/1/19 4:19 PM, Sowjanya Komatineni wrote:
>
> On 8/1/19 2:30 PM, Sowjanya Komatineni wrote:
>>
>> On 8/1/19 1:54 PM, Dmitry Osipenko wrote:
>>> 01.08.2019 23:31, Sowjanya Komatineni пишет:
>>>> On 8/1/19 1:17 PM, Dmitry Osipenko wrote:
>>>>> 01.08.2019 22:42, Sowjanya Komatineni пишет:
>>>>>> On 8/1/19 12:00 PM, Dmitry Osipenko wrote:
>>>>>>> 01.08.2019 20:58, Sowjanya Komatineni пишет:
>>>>>>>> On 7/31/19 4:09 PM, Sowjanya Komatineni wrote:
>>>>>>>>> On 7/31/19 3:44 AM, Dmitry Osipenko wrote:
>>>>>>>>>> 31.07.2019 12:50, Dmitry Osipenko пишет:
>>>>>>>>>>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>>>>>>>>>>> This patch implements save and restore context for peripheral
>>>>>>>>>>>> fixed
>>>>>>>>>>>> clock ops, peripheral gate clock ops, sdmmc mux clock ops, and
>>>>>>>>>>>> peripheral clock ops.
>>>>>>>>>>>>
>>>>>>>>>>>> During system suspend, core power goes off and looses the 
>>>>>>>>>>>> settings
>>>>>>>>>>>> of the Tegra CAR controller registers.
>>>>>>>>>>>>
>>>>>>>>>>>> So during suspend entry clock and reset state of 
>>>>>>>>>>>> peripherals is
>>>>>>>>>>>> saved
>>>>>>>>>>>> and on resume they are restored to have clocks back to same
>>>>>>>>>>>> rate and
>>>>>>>>>>>> state as before suspend.
>>>>>>>>>>>>
>>>>>>>>>>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>>>>>>>>>>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>>>>>>>>>>>> ---
>>>>>>>>>>>>      drivers/clk/tegra/clk-periph-fixed.c | 33
>>>>>>>>>>>> ++++++++++++++++++++++++++++++++
>>>>>>>>>>>>      drivers/clk/tegra/clk-periph-gate.c  | 34
>>>>>>>>>>>> +++++++++++++++++++++++++++++++++
>>>>>>>>>>>>      drivers/clk/tegra/clk-periph.c       | 37
>>>>>>>>>>>> ++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>>      drivers/clk/tegra/clk-sdmmc-mux.c    | 28
>>>>>>>>>>>> +++++++++++++++++++++++++++
>>>>>>>>>>>>      drivers/clk/tegra/clk.h              | 6 ++++++
>>>>>>>>>>>>      5 files changed, 138 insertions(+)
>>>>>>>>>>>>
>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>> index c088e7a280df..21b24530fa00 100644
>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>> @@ -60,11 +60,44 @@ tegra_clk_periph_fixed_recalc_rate(struct
>>>>>>>>>>>> clk_hw *hw,
>>>>>>>>>>>>          return (unsigned long)rate;
>>>>>>>>>>>>      }
>>>>>>>>>>>>      +static int tegra_clk_periph_fixed_save_context(struct 
>>>>>>>>>>>> clk_hw
>>>>>>>>>>>> *hw)
>>>>>>>>>>>> +{
>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>> +
>>>>>>>>>>>> +    fixed->enb_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>> fixed->regs->enb_reg) &
>>>>>>>>>>>> +             mask;
>>>>>>>>>>>> +    fixed->rst_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>> fixed->regs->rst_reg) &
>>>>>>>>>>>> +             mask;
>>>>>>>>>>>> +
>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>> +}
>>>>>>>>>>>> +
>>>>>>>>>>>> +static void tegra_clk_periph_fixed_restore_context(struct 
>>>>>>>>>>>> clk_hw
>>>>>>>>>>>> *hw)
>>>>>>>>>>>> +{
>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>> +
>>>>>>>>>>>> +    if (fixed->enb_ctx)
>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>> fixed->regs->enb_set_reg);
>>>>>>>>>>>> +    else
>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>> fixed->regs->enb_clr_reg);
>>>>>>>>>>>> +
>>>>>>>>>>>> +    udelay(2);
>>>>>>>>>>>> +
>>>>>>>>>>>> +    if (!fixed->rst_ctx) {
>>>>>>>>>>>> +        udelay(5); /* reset propogation delay */
>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base + 
>>>>>>>>>>>> fixed->regs->rst_reg);
>>>>>>>>>>>> +    }
>>>>>>>>>>>> +}
>>>>>>>>>>>> +
>>>>>>>>>>>>      static const struct clk_ops tegra_clk_periph_fixed_ops 
>>>>>>>>>>>> = {
>>>>>>>>>>>>          .is_enabled = tegra_clk_periph_fixed_is_enabled,
>>>>>>>>>>>>          .enable = tegra_clk_periph_fixed_enable,
>>>>>>>>>>>>          .disable = tegra_clk_periph_fixed_disable,
>>>>>>>>>>>>          .recalc_rate = tegra_clk_periph_fixed_recalc_rate,
>>>>>>>>>>>> +    .save_context = tegra_clk_periph_fixed_save_context,
>>>>>>>>>>>> +    .restore_context = 
>>>>>>>>>>>> tegra_clk_periph_fixed_restore_context,
>>>>>>>>>>>>      };
>>>>>>>>>>>>        struct clk *tegra_clk_register_periph_fixed(const char
>>>>>>>>>>>> *name,
>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>> index 4b31beefc9fc..6ba5b08e0787 100644
>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>> @@ -25,6 +25,8 @@ static DEFINE_SPINLOCK(periph_ref_lock);
>>>>>>>>>>>>        #define read_rst(gate) \
>>>>>>>>>>>>          readl_relaxed(gate->clk_base + (gate->regs->rst_reg))
>>>>>>>>>>>> +#define write_rst_set(val, gate) \
>>>>>>>>>>>> +    writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>> (gate->regs->rst_set_reg))
>>>>>>>>>>>>      #define write_rst_clr(val, gate) \
>>>>>>>>>>>>          writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>> (gate->regs->rst_clr_reg))
>>>>>>>>>>>>      @@ -110,10 +112,42 @@ static void 
>>>>>>>>>>>> clk_periph_disable(struct
>>>>>>>>>>>> clk_hw *hw)
>>>>>>>>>>>> spin_unlock_irqrestore(&periph_ref_lock, flags);
>>>>>>>>>>>>      }
>>>>>>>>>>>>      +static int clk_periph_gate_save_context(struct clk_hw 
>>>>>>>>>>>> *hw)
>>>>>>>>>>>> +{
>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate = 
>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>> +
>>>>>>>>>>>> +    gate->clk_state_ctx = read_enb(gate) &
>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>> +    gate->rst_state_ctx = read_rst(gate) &
>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>> +
>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>> +}
>>>>>>>>>>>> +
>>>>>>>>>>>> +static void clk_periph_gate_restore_context(struct clk_hw 
>>>>>>>>>>>> *hw)
>>>>>>>>>>>> +{
>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate = 
>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>> +
>>>>>>>>>>>> +    if (gate->clk_state_ctx)
>>>>>>>>>>>> + write_enb_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>> +    else
>>>>>>>>>>>> + write_enb_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>> +
>>>>>>>>>>>> +    udelay(5);
>>>>>>>>>>>> +
>>>>>>>>>>>> +    if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
>>>>>>>>>>>> +        !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
>>>>>>>>>>>> +        if (gate->rst_state_ctx)
>>>>>>>>>>>> + write_rst_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>> +        else
>>>>>>>>>>>> + write_rst_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>> +    }
>>>>>>>>>>>> +}
>>>>>>>>>>>> +
>>>>>>>>>>>>      const struct clk_ops tegra_clk_periph_gate_ops = {
>>>>>>>>>>>>          .is_enabled = clk_periph_is_enabled,
>>>>>>>>>>>>          .enable = clk_periph_enable,
>>>>>>>>>>>>          .disable = clk_periph_disable,
>>>>>>>>>>>> +    .save_context = clk_periph_gate_save_context,
>>>>>>>>>>>> +    .restore_context = clk_periph_gate_restore_context,
>>>>>>>>>>>>      };
>>>>>>>>>>>>        struct clk *tegra_clk_register_periph_gate(const 
>>>>>>>>>>>> char *name,
>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>> index 58437da25156..06fb62955768 100644
>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>> @@ -99,6 +99,37 @@ static void clk_periph_disable(struct 
>>>>>>>>>>>> clk_hw
>>>>>>>>>>>> *hw)
>>>>>>>>>>>>          gate_ops->disable(gate_hw);
>>>>>>>>>>>>      }
>>>>>>>>>>>>      +static int clk_periph_save_context(struct clk_hw *hw)
>>>>>>>>>>>> +{
>>>>>>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>> +
>>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
>>>>>>>>>>>> +        gate_ops->save_context(gate_hw);
>>>>>>>>>>>> +
>>>>>>>>>>>> +    periph->parent_ctx = clk_periph_get_parent(hw);
>>>>>>>>>>>> +
>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>> +}
>>>>>>>>>>>> +
>>>>>>>>>>>> +static void clk_periph_restore_context(struct clk_hw *hw)
>>>>>>>>>>>> +{
>>>>>>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>> +    const struct clk_ops *div_ops = periph->div_ops;
>>>>>>>>>>>> +    struct clk_hw *div_hw = &periph->divider.hw;
>>>>>>>>>>>> +
>>>>>>>>>>>> +    clk_periph_set_parent(hw, periph->parent_ctx);
>>>>>>>>>>>> +
>>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
>>>>>>>>>>>> + div_ops->restore_context(div_hw);
>>>>>>>>>>> Could you please point to where the divider's save_context()
>>>>>>>>>>> happens?
>>>>>>>>>>> Because I can't see it.
>>>>>>>>>> Ah, I now see that there is no need to save the dividers context
>>>>>>>>>> because
>>>>>>>>>> clk itself has enough info that is needed for the context's
>>>>>>>>>> restoring
>>>>>>>>>> (like I pointed in the review to v6).
>>>>>>>>>>
>>>>>>>>>> Looks like you could also implement a new 
>>>>>>>>>> clk_hw_get_parent_index()
>>>>>>>>>> generic helper to get the index instead of storing it manually.
>>>>>>>>> clk_periph_get_parent basically invokes existing clk_mux_ops
>>>>>>>>> get_parent() which is then saved in tegra_clk_periph.
>>>>>>>>>
>>>>>>>>> All existing drivers are using directly get_parent() from clk_mux
>>>>>>>>> which actually gets index from the register read.
>>>>>>>>>
>>>>>>>>> To have this more generic w.r.t save/restore context point of 
>>>>>>>>> view,
>>>>>>>>> probably instead of implementing new get_parent_index helper, 
>>>>>>>>> I think
>>>>>>>>> its better to implement save_context and restore_context to
>>>>>>>>> clk_mux_ops along with creating parent_index field into 
>>>>>>>>> clk_mux to
>>>>>>>>> cache index during set_parent.
>>>>>>>>>
>>>>>>>>> So we just need to invoke mux_ops save_context and 
>>>>>>>>> restore_context.
>>>>>>>>>
>>>>>>>> I hope its ok to add save/restore context to clk_mux_ops to be 
>>>>>>>> more
>>>>>>>> generic w.r.t save/restore context rather than get_parent_index 
>>>>>>>> API.
>>>>>>>> Please confirm if you agree.
>>>>>>> Sounds like a good idea. I see that there is a 'restoring' 
>>>>>>> helper for
>>>>>>> the generic clk_gate, seems something similar could be done for the
>>>>>>> clk_mux. And looks like anyway you'll need to associate the parent
>>>>>>> clock
>>>>>>> with the hw index in order to restore the muxing.
>>>>>> by 'restoring' helper for generic clk_gate, are you referring to
>>>>>> clk_gate_restore_context API?
>>>>> Yes.
>>>>>
>>>>>> clk_gate_restore_context is API that's any clk drivers can use for
>>>>>> clk_gate operation restore for custom gate clk_ops.
>>>>>>
>>>>>> But clk-periph is directly using generic clk_mux ops from clk_mux 
>>>>>> so I
>>>>>> think we should add .restore_context to clk_mux_ops and then during
>>>>>> clk-periph restore need to invoke mux_ops->restore_context.
>>>>> I'm not sure whether it will be good for every driver that uses 
>>>>> generic
>>>>> clk_mux ops. Should be more flexible to have a generic helper 
>>>>> function
>>>>> that any driver could use in order to restore the clock's parent.
>>>>>
>>>>> The clk-periph restoring also includes case of combining divider and
>>>>> parent restoring, so generic helper could be useful in that case 
>>>>> as well.
>>>>>
>>>>> It also looks like you could actually use the 
>>>>> clk_gate_restore_context()
>>>>> instead of manually saving the clock's enable-state, couldn't you?
>>>> ok for clk_mux, can add generic clk_mux_restore_context API rather 
>>>> than
>>>> using restore_context in clk_ops and will invoke that during 
>>>> clk_periph
>>>> restore.
>>>>
digging thru looks like for clk_periph source restore instead of 
clk_mux_restore_context, i can directly do clk_hw_get_parent and 
clk_set_parent with mux_hw as they invoke mux_ops get/set parent anyway. 
Will do this for periph clk mux
>>>>
>>>> Reg clk_gate, looks like we cant use generic clk_gate_restore_context
>>>> for clk-periph as it calls enable/disable callbacks and
>>>> clk_periph_enable/disable in clk-periph-gate also updated refcnt and
>>>> depending on that actual enable/disable is set.
>>>>
>>>> During suspend, peripherals that are already enabled have their 
>>>> refcnt >
>>>> 1, so they dont go thru enable/disable on restore if we use same
>>>> enable/disable callback.
>>> Looks like you could just decrement the gate's enable_refcnt on
>>> save_context, wouldn't that work?
>>>
> gate->enable_refcnt is within clk-periph-gate which gets updated when 
> enable/disable callbacks get execute thru clk_core_enable/disable.
> But actual enable_count used in clk_gate_restore_context is the one 
> which gets updated with in the clk core enable/disable functions which 
> invokes these callbacks. Depending on this enable_count in clk core it 
> invokes enable/disable.
>
> So, this will cause mismatch if we handle refcnt during save/restore 
> of tegra_clk_periph_gate_ops and also enable/disable thru this 
> clk_gate_restore_context is based on enable_count from clk core.
>
>>>> Also to align exact reset state along with CLK (like for case where 
>>>> CLK
>>>> is enabled but peripheral might be in reset state), implemented
>>>> save/restore in tegra specific tegra_clk_periph_gate_ops
>>> I'm wondering whether instead of saving/restoring reset-state of every
>>> clock, you could simply save/restore the whole RST_DEV_x_SET register.
>>> Couldn't you?
>> Thats what I was doing in first version of patch. But later as we 
>> moved to use clk_save_context and clk_restore_context, peripheral 
>> clk_hw RST & CLK enables happen thru its corresponding save/restore 
>> after source restore
>
>
> Also, to align both CLK & RST to the exact state of register, doing 
> save/restore in tegra_clk_periph_gate_ops and invoking this after 
> source restore for peripheral clock, seems cleaner to avoid any 
> misconfiguration b/w rst & clk settings.
>

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-07-31  0:20 ` [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support Sowjanya Komatineni
  2019-07-31  9:50   ` Dmitry Osipenko
@ 2019-08-02 12:32   ` Dmitry Osipenko
  2019-08-02 18:43     ` Sowjanya Komatineni
  1 sibling, 1 reply; 67+ messages in thread
From: Dmitry Osipenko @ 2019-08-02 12:32 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree

31.07.2019 3:20, Sowjanya Komatineni пишет:
> This patch implements save and restore context for peripheral fixed
> clock ops, peripheral gate clock ops, sdmmc mux clock ops, and
> peripheral clock ops.
> 
> During system suspend, core power goes off and looses the settings
> of the Tegra CAR controller registers.
> 
> So during suspend entry clock and reset state of peripherals is saved
> and on resume they are restored to have clocks back to same rate and
> state as before suspend.
> 
> Acked-by: Thierry Reding <treding@nvidia.com>
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> ---
>  drivers/clk/tegra/clk-periph-fixed.c | 33 ++++++++++++++++++++++++++++++++
>  drivers/clk/tegra/clk-periph-gate.c  | 34 +++++++++++++++++++++++++++++++++
>  drivers/clk/tegra/clk-periph.c       | 37 ++++++++++++++++++++++++++++++++++++
>  drivers/clk/tegra/clk-sdmmc-mux.c    | 28 +++++++++++++++++++++++++++
>  drivers/clk/tegra/clk.h              |  6 ++++++
>  5 files changed, 138 insertions(+)
> 
> diff --git a/drivers/clk/tegra/clk-periph-fixed.c b/drivers/clk/tegra/clk-periph-fixed.c
> index c088e7a280df..21b24530fa00 100644
> --- a/drivers/clk/tegra/clk-periph-fixed.c
> +++ b/drivers/clk/tegra/clk-periph-fixed.c
> @@ -60,11 +60,44 @@ tegra_clk_periph_fixed_recalc_rate(struct clk_hw *hw,
>  	return (unsigned long)rate;
>  }
>  
> +static int tegra_clk_periph_fixed_save_context(struct clk_hw *hw)
> +{
> +	struct tegra_clk_periph_fixed *fixed = to_tegra_clk_periph_fixed(hw);
> +	u32 mask = 1 << (fixed->num % 32);

This could be BIT(fixed->num % 32).

> +	fixed->enb_ctx = readl_relaxed(fixed->base + fixed->regs->enb_reg) &
> +			 mask;
> +	fixed->rst_ctx = readl_relaxed(fixed->base + fixed->regs->rst_reg) &
> +			 mask;

The enb_ctx/rst_ctx are booleans, while you assigning an integer value
here. You're getting away here because bool is an 32bit unsigned int,
but you shouldn't rely on it and always explicitly convert to a bool.

> +	return 0;
> +}
> +
> +static void tegra_clk_periph_fixed_restore_context(struct clk_hw *hw)
> +{
> +	struct tegra_clk_periph_fixed *fixed = to_tegra_clk_periph_fixed(hw);
> +	u32 mask = 1 << (fixed->num % 32);
> +
> +	if (fixed->enb_ctx)
> +		writel_relaxed(mask, fixed->base + fixed->regs->enb_set_reg);
> +	else
> +		writel_relaxed(mask, fixed->base + fixed->regs->enb_clr_reg);
> +
> +	udelay(2);

Will be better to read out and compare the hardware's state with the
restored one, then bail out if the state is unchanged.

Shouldn't it be fence_udelay()?

> +	if (!fixed->rst_ctx) {
> +		udelay(5); /* reset propogation delay */

Why delaying is done before the writing to the reset register?

> +		writel_relaxed(mask, fixed->base + fixed->regs->rst_reg);

I'm not quite sure what's going on here, this looks wrong.

1. rst_reg points to RST_DEVICES_x
2. Each bit of RST_DEVICES_x represents the reset-assertion state of
each individual device
3. By writing to rst_reg, all (!) devices are deasserted, except the one
device which corresponds to the mask
4. The reset is asserted for a single device, while !fixed->rst_ctx
means that it actually should be deasserted (?)

Apparently you should use rst_set_reg / rst_clr_reg.

> +	}

What about the case where rst_ctx=true?

> +}

> @@ -517,6 +517,8 @@ struct tegra_clk_periph_gate {
>  	int			clk_num;
>  	int			*enable_refcnt;
>  	const struct tegra_clk_periph_regs *regs;
> +	bool			clk_state_ctx;
> +	bool			rst_state_ctx;
>  };
>  
>  #define to_clk_periph_gate(_hw)					\
> @@ -543,6 +545,8 @@ struct tegra_clk_periph_fixed {
>  	unsigned int mul;
>  	unsigned int div;
>  	unsigned int num;
> +	bool enb_ctx;
> +	bool rst_ctx;
>  };

I'd expect these to be bool:1.

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-08-01 23:49                         ` Sowjanya Komatineni
@ 2019-08-02 12:38                           ` Dmitry Osipenko
  2019-08-02 18:33                             ` Sowjanya Komatineni
  0 siblings, 1 reply; 67+ messages in thread
From: Dmitry Osipenko @ 2019-08-02 12:38 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree

02.08.2019 2:49, Sowjanya Komatineni пишет:
> 
> On 8/1/19 4:19 PM, Sowjanya Komatineni wrote:
>>
>> On 8/1/19 2:30 PM, Sowjanya Komatineni wrote:
>>>
>>> On 8/1/19 1:54 PM, Dmitry Osipenko wrote:
>>>> 01.08.2019 23:31, Sowjanya Komatineni пишет:
>>>>> On 8/1/19 1:17 PM, Dmitry Osipenko wrote:
>>>>>> 01.08.2019 22:42, Sowjanya Komatineni пишет:
>>>>>>> On 8/1/19 12:00 PM, Dmitry Osipenko wrote:
>>>>>>>> 01.08.2019 20:58, Sowjanya Komatineni пишет:
>>>>>>>>> On 7/31/19 4:09 PM, Sowjanya Komatineni wrote:
>>>>>>>>>> On 7/31/19 3:44 AM, Dmitry Osipenko wrote:
>>>>>>>>>>> 31.07.2019 12:50, Dmitry Osipenko пишет:
>>>>>>>>>>>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>>>>>>>>>>>> This patch implements save and restore context for peripheral
>>>>>>>>>>>>> fixed
>>>>>>>>>>>>> clock ops, peripheral gate clock ops, sdmmc mux clock ops, and
>>>>>>>>>>>>> peripheral clock ops.
>>>>>>>>>>>>>
>>>>>>>>>>>>> During system suspend, core power goes off and looses the
>>>>>>>>>>>>> settings
>>>>>>>>>>>>> of the Tegra CAR controller registers.
>>>>>>>>>>>>>
>>>>>>>>>>>>> So during suspend entry clock and reset state of
>>>>>>>>>>>>> peripherals is
>>>>>>>>>>>>> saved
>>>>>>>>>>>>> and on resume they are restored to have clocks back to same
>>>>>>>>>>>>> rate and
>>>>>>>>>>>>> state as before suspend.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>>>>>>>>>>>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>>>>>>>>>>>>> ---
>>>>>>>>>>>>>      drivers/clk/tegra/clk-periph-fixed.c | 33
>>>>>>>>>>>>> ++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>      drivers/clk/tegra/clk-periph-gate.c  | 34
>>>>>>>>>>>>> +++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>      drivers/clk/tegra/clk-periph.c       | 37
>>>>>>>>>>>>> ++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>      drivers/clk/tegra/clk-sdmmc-mux.c    | 28
>>>>>>>>>>>>> +++++++++++++++++++++++++++
>>>>>>>>>>>>>      drivers/clk/tegra/clk.h              | 6 ++++++
>>>>>>>>>>>>>      5 files changed, 138 insertions(+)
>>>>>>>>>>>>>
>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>> index c088e7a280df..21b24530fa00 100644
>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>> @@ -60,11 +60,44 @@ tegra_clk_periph_fixed_recalc_rate(struct
>>>>>>>>>>>>> clk_hw *hw,
>>>>>>>>>>>>>          return (unsigned long)rate;
>>>>>>>>>>>>>      }
>>>>>>>>>>>>>      +static int tegra_clk_periph_fixed_save_context(struct
>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>> +{
>>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    fixed->enb_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>>> fixed->regs->enb_reg) &
>>>>>>>>>>>>> +             mask;
>>>>>>>>>>>>> +    fixed->rst_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>>> fixed->regs->rst_reg) &
>>>>>>>>>>>>> +             mask;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>> +}
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +static void tegra_clk_periph_fixed_restore_context(struct
>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>> +{
>>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    if (fixed->enb_ctx)
>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>> fixed->regs->enb_set_reg);
>>>>>>>>>>>>> +    else
>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>> fixed->regs->enb_clr_reg);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    udelay(2);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    if (!fixed->rst_ctx) {
>>>>>>>>>>>>> +        udelay(5); /* reset propogation delay */
>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>> fixed->regs->rst_reg);
>>>>>>>>>>>>> +    }
>>>>>>>>>>>>> +}
>>>>>>>>>>>>> +
>>>>>>>>>>>>>      static const struct clk_ops tegra_clk_periph_fixed_ops
>>>>>>>>>>>>> = {
>>>>>>>>>>>>>          .is_enabled = tegra_clk_periph_fixed_is_enabled,
>>>>>>>>>>>>>          .enable = tegra_clk_periph_fixed_enable,
>>>>>>>>>>>>>          .disable = tegra_clk_periph_fixed_disable,
>>>>>>>>>>>>>          .recalc_rate = tegra_clk_periph_fixed_recalc_rate,
>>>>>>>>>>>>> +    .save_context = tegra_clk_periph_fixed_save_context,
>>>>>>>>>>>>> +    .restore_context =
>>>>>>>>>>>>> tegra_clk_periph_fixed_restore_context,
>>>>>>>>>>>>>      };
>>>>>>>>>>>>>        struct clk *tegra_clk_register_periph_fixed(const char
>>>>>>>>>>>>> *name,
>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>> index 4b31beefc9fc..6ba5b08e0787 100644
>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>> @@ -25,6 +25,8 @@ static DEFINE_SPINLOCK(periph_ref_lock);
>>>>>>>>>>>>>        #define read_rst(gate) \
>>>>>>>>>>>>>          readl_relaxed(gate->clk_base + (gate->regs->rst_reg))
>>>>>>>>>>>>> +#define write_rst_set(val, gate) \
>>>>>>>>>>>>> +    writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>>> (gate->regs->rst_set_reg))
>>>>>>>>>>>>>      #define write_rst_clr(val, gate) \
>>>>>>>>>>>>>          writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>>> (gate->regs->rst_clr_reg))
>>>>>>>>>>>>>      @@ -110,10 +112,42 @@ static void
>>>>>>>>>>>>> clk_periph_disable(struct
>>>>>>>>>>>>> clk_hw *hw)
>>>>>>>>>>>>> spin_unlock_irqrestore(&periph_ref_lock, flags);
>>>>>>>>>>>>>      }
>>>>>>>>>>>>>      +static int clk_periph_gate_save_context(struct clk_hw
>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>> +{
>>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate =
>>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    gate->clk_state_ctx = read_enb(gate) &
>>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>>> +    gate->rst_state_ctx = read_rst(gate) &
>>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>> +}
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +static void clk_periph_gate_restore_context(struct clk_hw
>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>> +{
>>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate =
>>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    if (gate->clk_state_ctx)
>>>>>>>>>>>>> + write_enb_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>> +    else
>>>>>>>>>>>>> + write_enb_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    udelay(5);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
>>>>>>>>>>>>> +        !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
>>>>>>>>>>>>> +        if (gate->rst_state_ctx)
>>>>>>>>>>>>> + write_rst_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>> +        else
>>>>>>>>>>>>> + write_rst_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>> +    }
>>>>>>>>>>>>> +}
>>>>>>>>>>>>> +
>>>>>>>>>>>>>      const struct clk_ops tegra_clk_periph_gate_ops = {
>>>>>>>>>>>>>          .is_enabled = clk_periph_is_enabled,
>>>>>>>>>>>>>          .enable = clk_periph_enable,
>>>>>>>>>>>>>          .disable = clk_periph_disable,
>>>>>>>>>>>>> +    .save_context = clk_periph_gate_save_context,
>>>>>>>>>>>>> +    .restore_context = clk_periph_gate_restore_context,
>>>>>>>>>>>>>      };
>>>>>>>>>>>>>        struct clk *tegra_clk_register_periph_gate(const
>>>>>>>>>>>>> char *name,
>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>> index 58437da25156..06fb62955768 100644
>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>> @@ -99,6 +99,37 @@ static void clk_periph_disable(struct
>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>          gate_ops->disable(gate_hw);
>>>>>>>>>>>>>      }
>>>>>>>>>>>>>      +static int clk_periph_save_context(struct clk_hw *hw)
>>>>>>>>>>>>> +{
>>>>>>>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
>>>>>>>>>>>>> +        gate_ops->save_context(gate_hw);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    periph->parent_ctx = clk_periph_get_parent(hw);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>> +}
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +static void clk_periph_restore_context(struct clk_hw *hw)
>>>>>>>>>>>>> +{
>>>>>>>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>>> +    const struct clk_ops *div_ops = periph->div_ops;
>>>>>>>>>>>>> +    struct clk_hw *div_hw = &periph->divider.hw;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    clk_periph_set_parent(hw, periph->parent_ctx);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
>>>>>>>>>>>>> + div_ops->restore_context(div_hw);
>>>>>>>>>>>> Could you please point to where the divider's save_context()
>>>>>>>>>>>> happens?
>>>>>>>>>>>> Because I can't see it.
>>>>>>>>>>> Ah, I now see that there is no need to save the dividers context
>>>>>>>>>>> because
>>>>>>>>>>> clk itself has enough info that is needed for the context's
>>>>>>>>>>> restoring
>>>>>>>>>>> (like I pointed in the review to v6).
>>>>>>>>>>>
>>>>>>>>>>> Looks like you could also implement a new
>>>>>>>>>>> clk_hw_get_parent_index()
>>>>>>>>>>> generic helper to get the index instead of storing it manually.
>>>>>>>>>> clk_periph_get_parent basically invokes existing clk_mux_ops
>>>>>>>>>> get_parent() which is then saved in tegra_clk_periph.
>>>>>>>>>>
>>>>>>>>>> All existing drivers are using directly get_parent() from clk_mux
>>>>>>>>>> which actually gets index from the register read.
>>>>>>>>>>
>>>>>>>>>> To have this more generic w.r.t save/restore context point of
>>>>>>>>>> view,
>>>>>>>>>> probably instead of implementing new get_parent_index helper,
>>>>>>>>>> I think
>>>>>>>>>> its better to implement save_context and restore_context to
>>>>>>>>>> clk_mux_ops along with creating parent_index field into
>>>>>>>>>> clk_mux to
>>>>>>>>>> cache index during set_parent.
>>>>>>>>>>
>>>>>>>>>> So we just need to invoke mux_ops save_context and
>>>>>>>>>> restore_context.
>>>>>>>>>>
>>>>>>>>> I hope its ok to add save/restore context to clk_mux_ops to be
>>>>>>>>> more
>>>>>>>>> generic w.r.t save/restore context rather than get_parent_index
>>>>>>>>> API.
>>>>>>>>> Please confirm if you agree.
>>>>>>>> Sounds like a good idea. I see that there is a 'restoring'
>>>>>>>> helper for
>>>>>>>> the generic clk_gate, seems something similar could be done for the
>>>>>>>> clk_mux. And looks like anyway you'll need to associate the parent
>>>>>>>> clock
>>>>>>>> with the hw index in order to restore the muxing.
>>>>>>> by 'restoring' helper for generic clk_gate, are you referring to
>>>>>>> clk_gate_restore_context API?
>>>>>> Yes.
>>>>>>
>>>>>>> clk_gate_restore_context is API that's any clk drivers can use for
>>>>>>> clk_gate operation restore for custom gate clk_ops.
>>>>>>>
>>>>>>> But clk-periph is directly using generic clk_mux ops from clk_mux
>>>>>>> so I
>>>>>>> think we should add .restore_context to clk_mux_ops and then during
>>>>>>> clk-periph restore need to invoke mux_ops->restore_context.
>>>>>> I'm not sure whether it will be good for every driver that uses
>>>>>> generic
>>>>>> clk_mux ops. Should be more flexible to have a generic helper
>>>>>> function
>>>>>> that any driver could use in order to restore the clock's parent.
>>>>>>
>>>>>> The clk-periph restoring also includes case of combining divider and
>>>>>> parent restoring, so generic helper could be useful in that case
>>>>>> as well.
>>>>>>
>>>>>> It also looks like you could actually use the
>>>>>> clk_gate_restore_context()
>>>>>> instead of manually saving the clock's enable-state, couldn't you?
>>>>> ok for clk_mux, can add generic clk_mux_restore_context API rather
>>>>> than
>>>>> using restore_context in clk_ops and will invoke that during
>>>>> clk_periph
>>>>> restore.
>>>>>
> digging thru looks like for clk_periph source restore instead of
> clk_mux_restore_context, i can directly do clk_hw_get_parent and
> clk_set_parent with mux_hw as they invoke mux_ops get/set parent anyway.
> Will do this for periph clk mux
>>>>>
>>>>> Reg clk_gate, looks like we cant use generic clk_gate_restore_context
>>>>> for clk-periph as it calls enable/disable callbacks and
>>>>> clk_periph_enable/disable in clk-periph-gate also updated refcnt and
>>>>> depending on that actual enable/disable is set.
>>>>>
>>>>> During suspend, peripherals that are already enabled have their
>>>>> refcnt >
>>>>> 1, so they dont go thru enable/disable on restore if we use same
>>>>> enable/disable callback.
>>>> Looks like you could just decrement the gate's enable_refcnt on
>>>> save_context, wouldn't that work?
>>>>
>> gate->enable_refcnt is within clk-periph-gate which gets updated when
>> enable/disable callbacks get execute thru clk_core_enable/disable.
>> But actual enable_count used in clk_gate_restore_context is the one
>> which gets updated with in the clk core enable/disable functions which
>> invokes these callbacks. Depending on this enable_count in clk core it
>> invokes enable/disable.
>>
>> So, this will cause mismatch if we handle refcnt during save/restore
>> of tegra_clk_periph_gate_ops and also enable/disable thru this
>> clk_gate_restore_context is based on enable_count from clk core.
>>
>>>>> Also to align exact reset state along with CLK (like for case where
>>>>> CLK
>>>>> is enabled but peripheral might be in reset state), implemented
>>>>> save/restore in tegra specific tegra_clk_periph_gate_ops
>>>> I'm wondering whether instead of saving/restoring reset-state of every
>>>> clock, you could simply save/restore the whole RST_DEV_x_SET register.
>>>> Couldn't you?
>>> Thats what I was doing in first version of patch. But later as we
>>> moved to use clk_save_context and clk_restore_context, peripheral
>>> clk_hw RST & CLK enables happen thru its corresponding save/restore
>>> after source restore
>>
>>
>> Also, to align both CLK & RST to the exact state of register, doing
>> save/restore in tegra_clk_periph_gate_ops and invoking this after
>> source restore for peripheral clock, seems cleaner to avoid any
>> misconfiguration b/w rst & clk settings.
>>

It looks to me that it is very wasteful to store/restore each individual
gate and reset state, also given that some of them are shared. I think
that the gates and resets should be restored separately for the
peripherals by a custom tegra_clk_save/restore_periph_gates/resets().

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-08-02 12:38                           ` Dmitry Osipenko
@ 2019-08-02 18:33                             ` Sowjanya Komatineni
  2019-08-02 20:13                               ` Dmitry Osipenko
  0 siblings, 1 reply; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-08-02 18:33 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree


On 8/2/19 5:38 AM, Dmitry Osipenko wrote:
> 02.08.2019 2:49, Sowjanya Komatineni пишет:
>> On 8/1/19 4:19 PM, Sowjanya Komatineni wrote:
>>> On 8/1/19 2:30 PM, Sowjanya Komatineni wrote:
>>>> On 8/1/19 1:54 PM, Dmitry Osipenko wrote:
>>>>> 01.08.2019 23:31, Sowjanya Komatineni пишет:
>>>>>> On 8/1/19 1:17 PM, Dmitry Osipenko wrote:
>>>>>>> 01.08.2019 22:42, Sowjanya Komatineni пишет:
>>>>>>>> On 8/1/19 12:00 PM, Dmitry Osipenko wrote:
>>>>>>>>> 01.08.2019 20:58, Sowjanya Komatineni пишет:
>>>>>>>>>> On 7/31/19 4:09 PM, Sowjanya Komatineni wrote:
>>>>>>>>>>> On 7/31/19 3:44 AM, Dmitry Osipenko wrote:
>>>>>>>>>>>> 31.07.2019 12:50, Dmitry Osipenko пишет:
>>>>>>>>>>>>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>> This patch implements save and restore context for peripheral
>>>>>>>>>>>>>> fixed
>>>>>>>>>>>>>> clock ops, peripheral gate clock ops, sdmmc mux clock ops, and
>>>>>>>>>>>>>> peripheral clock ops.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> During system suspend, core power goes off and looses the
>>>>>>>>>>>>>> settings
>>>>>>>>>>>>>> of the Tegra CAR controller registers.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> So during suspend entry clock and reset state of
>>>>>>>>>>>>>> peripherals is
>>>>>>>>>>>>>> saved
>>>>>>>>>>>>>> and on resume they are restored to have clocks back to same
>>>>>>>>>>>>>> rate and
>>>>>>>>>>>>>> state as before suspend.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>>>>>>>>>>>>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>       drivers/clk/tegra/clk-periph-fixed.c | 33
>>>>>>>>>>>>>> ++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>       drivers/clk/tegra/clk-periph-gate.c  | 34
>>>>>>>>>>>>>> +++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>       drivers/clk/tegra/clk-periph.c       | 37
>>>>>>>>>>>>>> ++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>       drivers/clk/tegra/clk-sdmmc-mux.c    | 28
>>>>>>>>>>>>>> +++++++++++++++++++++++++++
>>>>>>>>>>>>>>       drivers/clk/tegra/clk.h              | 6 ++++++
>>>>>>>>>>>>>>       5 files changed, 138 insertions(+)
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>> index c088e7a280df..21b24530fa00 100644
>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>> @@ -60,11 +60,44 @@ tegra_clk_periph_fixed_recalc_rate(struct
>>>>>>>>>>>>>> clk_hw *hw,
>>>>>>>>>>>>>>           return (unsigned long)rate;
>>>>>>>>>>>>>>       }
>>>>>>>>>>>>>>       +static int tegra_clk_periph_fixed_save_context(struct
>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    fixed->enb_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>>>> fixed->regs->enb_reg) &
>>>>>>>>>>>>>> +             mask;
>>>>>>>>>>>>>> +    fixed->rst_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>>>> fixed->regs->rst_reg) &
>>>>>>>>>>>>>> +             mask;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +static void tegra_clk_periph_fixed_restore_context(struct
>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    if (fixed->enb_ctx)
>>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>> fixed->regs->enb_set_reg);
>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>> fixed->regs->enb_clr_reg);
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    udelay(2);
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    if (!fixed->rst_ctx) {
>>>>>>>>>>>>>> +        udelay(5); /* reset propogation delay */
>>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>> fixed->regs->rst_reg);
>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>       static const struct clk_ops tegra_clk_periph_fixed_ops
>>>>>>>>>>>>>> = {
>>>>>>>>>>>>>>           .is_enabled = tegra_clk_periph_fixed_is_enabled,
>>>>>>>>>>>>>>           .enable = tegra_clk_periph_fixed_enable,
>>>>>>>>>>>>>>           .disable = tegra_clk_periph_fixed_disable,
>>>>>>>>>>>>>>           .recalc_rate = tegra_clk_periph_fixed_recalc_rate,
>>>>>>>>>>>>>> +    .save_context = tegra_clk_periph_fixed_save_context,
>>>>>>>>>>>>>> +    .restore_context =
>>>>>>>>>>>>>> tegra_clk_periph_fixed_restore_context,
>>>>>>>>>>>>>>       };
>>>>>>>>>>>>>>         struct clk *tegra_clk_register_periph_fixed(const char
>>>>>>>>>>>>>> *name,
>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>> index 4b31beefc9fc..6ba5b08e0787 100644
>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>> @@ -25,6 +25,8 @@ static DEFINE_SPINLOCK(periph_ref_lock);
>>>>>>>>>>>>>>         #define read_rst(gate) \
>>>>>>>>>>>>>>           readl_relaxed(gate->clk_base + (gate->regs->rst_reg))
>>>>>>>>>>>>>> +#define write_rst_set(val, gate) \
>>>>>>>>>>>>>> +    writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>>>> (gate->regs->rst_set_reg))
>>>>>>>>>>>>>>       #define write_rst_clr(val, gate) \
>>>>>>>>>>>>>>           writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>>>> (gate->regs->rst_clr_reg))
>>>>>>>>>>>>>>       @@ -110,10 +112,42 @@ static void
>>>>>>>>>>>>>> clk_periph_disable(struct
>>>>>>>>>>>>>> clk_hw *hw)
>>>>>>>>>>>>>> spin_unlock_irqrestore(&periph_ref_lock, flags);
>>>>>>>>>>>>>>       }
>>>>>>>>>>>>>>       +static int clk_periph_gate_save_context(struct clk_hw
>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate =
>>>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    gate->clk_state_ctx = read_enb(gate) &
>>>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>>>> +    gate->rst_state_ctx = read_rst(gate) &
>>>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +static void clk_periph_gate_restore_context(struct clk_hw
>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate =
>>>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    if (gate->clk_state_ctx)
>>>>>>>>>>>>>> + write_enb_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>> + write_enb_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    udelay(5);
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
>>>>>>>>>>>>>> +        !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
>>>>>>>>>>>>>> +        if (gate->rst_state_ctx)
>>>>>>>>>>>>>> + write_rst_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>> +        else
>>>>>>>>>>>>>> + write_rst_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>       const struct clk_ops tegra_clk_periph_gate_ops = {
>>>>>>>>>>>>>>           .is_enabled = clk_periph_is_enabled,
>>>>>>>>>>>>>>           .enable = clk_periph_enable,
>>>>>>>>>>>>>>           .disable = clk_periph_disable,
>>>>>>>>>>>>>> +    .save_context = clk_periph_gate_save_context,
>>>>>>>>>>>>>> +    .restore_context = clk_periph_gate_restore_context,
>>>>>>>>>>>>>>       };
>>>>>>>>>>>>>>         struct clk *tegra_clk_register_periph_gate(const
>>>>>>>>>>>>>> char *name,
>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>> index 58437da25156..06fb62955768 100644
>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>> @@ -99,6 +99,37 @@ static void clk_periph_disable(struct
>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>           gate_ops->disable(gate_hw);
>>>>>>>>>>>>>>       }
>>>>>>>>>>>>>>       +static int clk_periph_save_context(struct clk_hw *hw)
>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
>>>>>>>>>>>>>> +        gate_ops->save_context(gate_hw);
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    periph->parent_ctx = clk_periph_get_parent(hw);
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +static void clk_periph_restore_context(struct clk_hw *hw)
>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>>>> +    const struct clk_ops *div_ops = periph->div_ops;
>>>>>>>>>>>>>> +    struct clk_hw *div_hw = &periph->divider.hw;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    clk_periph_set_parent(hw, periph->parent_ctx);
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
>>>>>>>>>>>>>> + div_ops->restore_context(div_hw);
>>>>>>>>>>>>> Could you please point to where the divider's save_context()
>>>>>>>>>>>>> happens?
>>>>>>>>>>>>> Because I can't see it.
>>>>>>>>>>>> Ah, I now see that there is no need to save the dividers context
>>>>>>>>>>>> because
>>>>>>>>>>>> clk itself has enough info that is needed for the context's
>>>>>>>>>>>> restoring
>>>>>>>>>>>> (like I pointed in the review to v6).
>>>>>>>>>>>>
>>>>>>>>>>>> Looks like you could also implement a new
>>>>>>>>>>>> clk_hw_get_parent_index()
>>>>>>>>>>>> generic helper to get the index instead of storing it manually.
>>>>>>>>>>> clk_periph_get_parent basically invokes existing clk_mux_ops
>>>>>>>>>>> get_parent() which is then saved in tegra_clk_periph.
>>>>>>>>>>>
>>>>>>>>>>> All existing drivers are using directly get_parent() from clk_mux
>>>>>>>>>>> which actually gets index from the register read.
>>>>>>>>>>>
>>>>>>>>>>> To have this more generic w.r.t save/restore context point of
>>>>>>>>>>> view,
>>>>>>>>>>> probably instead of implementing new get_parent_index helper,
>>>>>>>>>>> I think
>>>>>>>>>>> its better to implement save_context and restore_context to
>>>>>>>>>>> clk_mux_ops along with creating parent_index field into
>>>>>>>>>>> clk_mux to
>>>>>>>>>>> cache index during set_parent.
>>>>>>>>>>>
>>>>>>>>>>> So we just need to invoke mux_ops save_context and
>>>>>>>>>>> restore_context.
>>>>>>>>>>>
>>>>>>>>>> I hope its ok to add save/restore context to clk_mux_ops to be
>>>>>>>>>> more
>>>>>>>>>> generic w.r.t save/restore context rather than get_parent_index
>>>>>>>>>> API.
>>>>>>>>>> Please confirm if you agree.
>>>>>>>>> Sounds like a good idea. I see that there is a 'restoring'
>>>>>>>>> helper for
>>>>>>>>> the generic clk_gate, seems something similar could be done for the
>>>>>>>>> clk_mux. And looks like anyway you'll need to associate the parent
>>>>>>>>> clock
>>>>>>>>> with the hw index in order to restore the muxing.
>>>>>>>> by 'restoring' helper for generic clk_gate, are you referring to
>>>>>>>> clk_gate_restore_context API?
>>>>>>> Yes.
>>>>>>>
>>>>>>>> clk_gate_restore_context is API that's any clk drivers can use for
>>>>>>>> clk_gate operation restore for custom gate clk_ops.
>>>>>>>>
>>>>>>>> But clk-periph is directly using generic clk_mux ops from clk_mux
>>>>>>>> so I
>>>>>>>> think we should add .restore_context to clk_mux_ops and then during
>>>>>>>> clk-periph restore need to invoke mux_ops->restore_context.
>>>>>>> I'm not sure whether it will be good for every driver that uses
>>>>>>> generic
>>>>>>> clk_mux ops. Should be more flexible to have a generic helper
>>>>>>> function
>>>>>>> that any driver could use in order to restore the clock's parent.
>>>>>>>
>>>>>>> The clk-periph restoring also includes case of combining divider and
>>>>>>> parent restoring, so generic helper could be useful in that case
>>>>>>> as well.
>>>>>>>
>>>>>>> It also looks like you could actually use the
>>>>>>> clk_gate_restore_context()
>>>>>>> instead of manually saving the clock's enable-state, couldn't you?
>>>>>> ok for clk_mux, can add generic clk_mux_restore_context API rather
>>>>>> than
>>>>>> using restore_context in clk_ops and will invoke that during
>>>>>> clk_periph
>>>>>> restore.
>>>>>>
>> digging thru looks like for clk_periph source restore instead of
>> clk_mux_restore_context, i can directly do clk_hw_get_parent and
>> clk_set_parent with mux_hw as they invoke mux_ops get/set parent anyway.
>> Will do this for periph clk mux
>>>>>> Reg clk_gate, looks like we cant use generic clk_gate_restore_context
>>>>>> for clk-periph as it calls enable/disable callbacks and
>>>>>> clk_periph_enable/disable in clk-periph-gate also updated refcnt and
>>>>>> depending on that actual enable/disable is set.
>>>>>>
>>>>>> During suspend, peripherals that are already enabled have their
>>>>>> refcnt >
>>>>>> 1, so they dont go thru enable/disable on restore if we use same
>>>>>> enable/disable callback.
>>>>> Looks like you could just decrement the gate's enable_refcnt on
>>>>> save_context, wouldn't that work?
>>>>>
>>> gate->enable_refcnt is within clk-periph-gate which gets updated when
>>> enable/disable callbacks get execute thru clk_core_enable/disable.
>>> But actual enable_count used in clk_gate_restore_context is the one
>>> which gets updated with in the clk core enable/disable functions which
>>> invokes these callbacks. Depending on this enable_count in clk core it
>>> invokes enable/disable.
>>>
>>> So, this will cause mismatch if we handle refcnt during save/restore
>>> of tegra_clk_periph_gate_ops and also enable/disable thru this
>>> clk_gate_restore_context is based on enable_count from clk core.
>>>
>>>>>> Also to align exact reset state along with CLK (like for case where
>>>>>> CLK
>>>>>> is enabled but peripheral might be in reset state), implemented
>>>>>> save/restore in tegra specific tegra_clk_periph_gate_ops
>>>>> I'm wondering whether instead of saving/restoring reset-state of every
>>>>> clock, you could simply save/restore the whole RST_DEV_x_SET register.
>>>>> Couldn't you?
>>>> Thats what I was doing in first version of patch. But later as we
>>>> moved to use clk_save_context and clk_restore_context, peripheral
>>>> clk_hw RST & CLK enables happen thru its corresponding save/restore
>>>> after source restore
>>>
>>> Also, to align both CLK & RST to the exact state of register, doing
>>> save/restore in tegra_clk_periph_gate_ops and invoking this after
>>> source restore for peripheral clock, seems cleaner to avoid any
>>> misconfiguration b/w rst & clk settings.
>>>
> It looks to me that it is very wasteful to store/restore each individual
> gate and reset state, also given that some of them are shared. I think
> that the gates and resets should be restored separately for the
> peripherals by a custom tegra_clk_save/restore_periph_gates/resets().
clk_periph_fixed_disable just disables clock only without deasserting 
the corresponding peripheral.

corresponding peripheral drivers can also issue reset assert/deassert 
thru reset_control_assert/deassert.

So, we will not get the actual state of clk and rst unless we read and 
save state of reset and clock separately during save_context.

Currently patch is already using custom 
tegra_clk_periph_fixed_save/restore_context for corresponding clk_ops.
Are you suggesting to do save and restore of complete CLK_ENB/RST_DEV 
register settings instead of individual peripheral bits?

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-08-02 12:32   ` Dmitry Osipenko
@ 2019-08-02 18:43     ` Sowjanya Komatineni
  2019-08-02 20:20       ` Dmitry Osipenko
  0 siblings, 1 reply; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-08-02 18:43 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree


On 8/2/19 5:32 AM, Dmitry Osipenko wrote:
> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>> This patch implements save and restore context for peripheral fixed
>> clock ops, peripheral gate clock ops, sdmmc mux clock ops, and
>> peripheral clock ops.
>>
>> During system suspend, core power goes off and looses the settings
>> of the Tegra CAR controller registers.
>>
>> So during suspend entry clock and reset state of peripherals is saved
>> and on resume they are restored to have clocks back to same rate and
>> state as before suspend.
>>
>> Acked-by: Thierry Reding <treding@nvidia.com>
>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>> ---
>>   drivers/clk/tegra/clk-periph-fixed.c | 33 ++++++++++++++++++++++++++++++++
>>   drivers/clk/tegra/clk-periph-gate.c  | 34 +++++++++++++++++++++++++++++++++
>>   drivers/clk/tegra/clk-periph.c       | 37 ++++++++++++++++++++++++++++++++++++
>>   drivers/clk/tegra/clk-sdmmc-mux.c    | 28 +++++++++++++++++++++++++++
>>   drivers/clk/tegra/clk.h              |  6 ++++++
>>   5 files changed, 138 insertions(+)
>>
>> diff --git a/drivers/clk/tegra/clk-periph-fixed.c b/drivers/clk/tegra/clk-periph-fixed.c
>> index c088e7a280df..21b24530fa00 100644
>> --- a/drivers/clk/tegra/clk-periph-fixed.c
>> +++ b/drivers/clk/tegra/clk-periph-fixed.c
>> @@ -60,11 +60,44 @@ tegra_clk_periph_fixed_recalc_rate(struct clk_hw *hw,
>>   	return (unsigned long)rate;
>>   }
>>   
>> +static int tegra_clk_periph_fixed_save_context(struct clk_hw *hw)
>> +{
>> +	struct tegra_clk_periph_fixed *fixed = to_tegra_clk_periph_fixed(hw);
>> +	u32 mask = 1 << (fixed->num % 32);
> This could be BIT(fixed->num % 32).
>
>> +	fixed->enb_ctx = readl_relaxed(fixed->base + fixed->regs->enb_reg) &
>> +			 mask;
>> +	fixed->rst_ctx = readl_relaxed(fixed->base + fixed->regs->rst_reg) &
>> +			 mask;
> The enb_ctx/rst_ctx are booleans, while you assigning an integer value
> here. You're getting away here because bool is an 32bit unsigned int,
> but you shouldn't rely on it and always explicitly convert to a bool.
>
>> +	return 0;
>> +}
>> +
>> +static void tegra_clk_periph_fixed_restore_context(struct clk_hw *hw)
>> +{
>> +	struct tegra_clk_periph_fixed *fixed = to_tegra_clk_periph_fixed(hw);
>> +	u32 mask = 1 << (fixed->num % 32);
>> +
>> +	if (fixed->enb_ctx)
>> +		writel_relaxed(mask, fixed->base + fixed->regs->enb_set_reg);
>> +	else
>> +		writel_relaxed(mask, fixed->base + fixed->regs->enb_clr_reg);
>> +
>> +	udelay(2);
> Will be better to read out and compare the hardware's state with the
> restored one, then bail out if the state is unchanged.
>
> Shouldn't it be fence_udelay()?
>
>> +	if (!fixed->rst_ctx) {
>> +		udelay(5); /* reset propogation delay */
> Why delaying is done before the writing to the reset register?

During SC7 exit, peripheral reset state is set to POR state. So some 
peripherals will already be in reset state and making sure of 
propagation delay before releasing from reset.

It should be rst_clr_reg. will fix in next rev

>
>> +		writel_relaxed(mask, fixed->base + fixed->regs->rst_reg);
> I'm not quite sure what's going on here, this looks wrong.
>
> 1. rst_reg points to RST_DEVICES_x
> 2. Each bit of RST_DEVICES_x represents the reset-assertion state of
> each individual device
> 3. By writing to rst_reg, all (!) devices are deasserted, except the one
> device which corresponds to the mask
> 4. The reset is asserted for a single device, while !fixed->rst_ctx
> means that it actually should be deasserted (?)
>
> Apparently you should use rst_set_reg / rst_clr_reg.
Yes, It should be rst_clr_reg. will fix in next rev
>> +	}
> What about the case where rst_ctx=true?

ON SC7 exit, state of RST_DEV will be POR state where most peripherals 
will already be in reset state.

Few of them which are not in reset state in POR values are those that 
need to stay de-asserted across the boot states anyway.

>
>> +}
>> @@ -517,6 +517,8 @@ struct tegra_clk_periph_gate {
>>   	int			clk_num;
>>   	int			*enable_refcnt;
>>   	const struct tegra_clk_periph_regs *regs;
>> +	bool			clk_state_ctx;
>> +	bool			rst_state_ctx;
>>   };
>>   
>>   #define to_clk_periph_gate(_hw)					\
>> @@ -543,6 +545,8 @@ struct tegra_clk_periph_fixed {
>>   	unsigned int mul;
>>   	unsigned int div;
>>   	unsigned int num;
>> +	bool enb_ctx;
>> +	bool rst_ctx;
>>   };
> I'd expect these to be bool:1.

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-08-02 18:33                             ` Sowjanya Komatineni
@ 2019-08-02 20:13                               ` Dmitry Osipenko
  2019-08-02 20:17                                 ` Dmitry Osipenko
  0 siblings, 1 reply; 67+ messages in thread
From: Dmitry Osipenko @ 2019-08-02 20:13 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree

02.08.2019 21:33, Sowjanya Komatineni пишет:
> 
> On 8/2/19 5:38 AM, Dmitry Osipenko wrote:
>> 02.08.2019 2:49, Sowjanya Komatineni пишет:
>>> On 8/1/19 4:19 PM, Sowjanya Komatineni wrote:
>>>> On 8/1/19 2:30 PM, Sowjanya Komatineni wrote:
>>>>> On 8/1/19 1:54 PM, Dmitry Osipenko wrote:
>>>>>> 01.08.2019 23:31, Sowjanya Komatineni пишет:
>>>>>>> On 8/1/19 1:17 PM, Dmitry Osipenko wrote:
>>>>>>>> 01.08.2019 22:42, Sowjanya Komatineni пишет:
>>>>>>>>> On 8/1/19 12:00 PM, Dmitry Osipenko wrote:
>>>>>>>>>> 01.08.2019 20:58, Sowjanya Komatineni пишет:
>>>>>>>>>>> On 7/31/19 4:09 PM, Sowjanya Komatineni wrote:
>>>>>>>>>>>> On 7/31/19 3:44 AM, Dmitry Osipenko wrote:
>>>>>>>>>>>>> 31.07.2019 12:50, Dmitry Osipenko пишет:
>>>>>>>>>>>>>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>> This patch implements save and restore context for
>>>>>>>>>>>>>>> peripheral
>>>>>>>>>>>>>>> fixed
>>>>>>>>>>>>>>> clock ops, peripheral gate clock ops, sdmmc mux clock
>>>>>>>>>>>>>>> ops, and
>>>>>>>>>>>>>>> peripheral clock ops.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> During system suspend, core power goes off and looses the
>>>>>>>>>>>>>>> settings
>>>>>>>>>>>>>>> of the Tegra CAR controller registers.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> So during suspend entry clock and reset state of
>>>>>>>>>>>>>>> peripherals is
>>>>>>>>>>>>>>> saved
>>>>>>>>>>>>>>> and on resume they are restored to have clocks back to same
>>>>>>>>>>>>>>> rate and
>>>>>>>>>>>>>>> state as before suspend.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>>>>>>>>>>>>>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>       drivers/clk/tegra/clk-periph-fixed.c | 33
>>>>>>>>>>>>>>> ++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>       drivers/clk/tegra/clk-periph-gate.c  | 34
>>>>>>>>>>>>>>> +++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>       drivers/clk/tegra/clk-periph.c       | 37
>>>>>>>>>>>>>>> ++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>       drivers/clk/tegra/clk-sdmmc-mux.c    | 28
>>>>>>>>>>>>>>> +++++++++++++++++++++++++++
>>>>>>>>>>>>>>>       drivers/clk/tegra/clk.h              | 6 ++++++
>>>>>>>>>>>>>>>       5 files changed, 138 insertions(+)
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>> index c088e7a280df..21b24530fa00 100644
>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>> @@ -60,11 +60,44 @@
>>>>>>>>>>>>>>> tegra_clk_periph_fixed_recalc_rate(struct
>>>>>>>>>>>>>>> clk_hw *hw,
>>>>>>>>>>>>>>>           return (unsigned long)rate;
>>>>>>>>>>>>>>>       }
>>>>>>>>>>>>>>>       +static int tegra_clk_periph_fixed_save_context(struct
>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    fixed->enb_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>>>>> fixed->regs->enb_reg) &
>>>>>>>>>>>>>>> +             mask;
>>>>>>>>>>>>>>> +    fixed->rst_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>>>>> fixed->regs->rst_reg) &
>>>>>>>>>>>>>>> +             mask;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +static void tegra_clk_periph_fixed_restore_context(struct
>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    if (fixed->enb_ctx)
>>>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>> fixed->regs->enb_set_reg);
>>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>> fixed->regs->enb_clr_reg);
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    udelay(2);
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    if (!fixed->rst_ctx) {
>>>>>>>>>>>>>>> +        udelay(5); /* reset propogation delay */
>>>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>> fixed->regs->rst_reg);
>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>       static const struct clk_ops tegra_clk_periph_fixed_ops
>>>>>>>>>>>>>>> = {
>>>>>>>>>>>>>>>           .is_enabled = tegra_clk_periph_fixed_is_enabled,
>>>>>>>>>>>>>>>           .enable = tegra_clk_periph_fixed_enable,
>>>>>>>>>>>>>>>           .disable = tegra_clk_periph_fixed_disable,
>>>>>>>>>>>>>>>           .recalc_rate = tegra_clk_periph_fixed_recalc_rate,
>>>>>>>>>>>>>>> +    .save_context = tegra_clk_periph_fixed_save_context,
>>>>>>>>>>>>>>> +    .restore_context =
>>>>>>>>>>>>>>> tegra_clk_periph_fixed_restore_context,
>>>>>>>>>>>>>>>       };
>>>>>>>>>>>>>>>         struct clk *tegra_clk_register_periph_fixed(const
>>>>>>>>>>>>>>> char
>>>>>>>>>>>>>>> *name,
>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>> index 4b31beefc9fc..6ba5b08e0787 100644
>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>> @@ -25,6 +25,8 @@ static DEFINE_SPINLOCK(periph_ref_lock);
>>>>>>>>>>>>>>>         #define read_rst(gate) \
>>>>>>>>>>>>>>>           readl_relaxed(gate->clk_base +
>>>>>>>>>>>>>>> (gate->regs->rst_reg))
>>>>>>>>>>>>>>> +#define write_rst_set(val, gate) \
>>>>>>>>>>>>>>> +    writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>>>>> (gate->regs->rst_set_reg))
>>>>>>>>>>>>>>>       #define write_rst_clr(val, gate) \
>>>>>>>>>>>>>>>           writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>>>>> (gate->regs->rst_clr_reg))
>>>>>>>>>>>>>>>       @@ -110,10 +112,42 @@ static void
>>>>>>>>>>>>>>> clk_periph_disable(struct
>>>>>>>>>>>>>>> clk_hw *hw)
>>>>>>>>>>>>>>> spin_unlock_irqrestore(&periph_ref_lock, flags);
>>>>>>>>>>>>>>>       }
>>>>>>>>>>>>>>>       +static int clk_periph_gate_save_context(struct clk_hw
>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate =
>>>>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    gate->clk_state_ctx = read_enb(gate) &
>>>>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>>>>> +    gate->rst_state_ctx = read_rst(gate) &
>>>>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +static void clk_periph_gate_restore_context(struct clk_hw
>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate =
>>>>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    if (gate->clk_state_ctx)
>>>>>>>>>>>>>>> + write_enb_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>>> + write_enb_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    udelay(5);
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
>>>>>>>>>>>>>>> +        !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
>>>>>>>>>>>>>>> +        if (gate->rst_state_ctx)
>>>>>>>>>>>>>>> + write_rst_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>> +        else
>>>>>>>>>>>>>>> + write_rst_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>       const struct clk_ops tegra_clk_periph_gate_ops = {
>>>>>>>>>>>>>>>           .is_enabled = clk_periph_is_enabled,
>>>>>>>>>>>>>>>           .enable = clk_periph_enable,
>>>>>>>>>>>>>>>           .disable = clk_periph_disable,
>>>>>>>>>>>>>>> +    .save_context = clk_periph_gate_save_context,
>>>>>>>>>>>>>>> +    .restore_context = clk_periph_gate_restore_context,
>>>>>>>>>>>>>>>       };
>>>>>>>>>>>>>>>         struct clk *tegra_clk_register_periph_gate(const
>>>>>>>>>>>>>>> char *name,
>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>> index 58437da25156..06fb62955768 100644
>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>> @@ -99,6 +99,37 @@ static void clk_periph_disable(struct
>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>           gate_ops->disable(gate_hw);
>>>>>>>>>>>>>>>       }
>>>>>>>>>>>>>>>       +static int clk_periph_save_context(struct clk_hw *hw)
>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
>>>>>>>>>>>>>>> +        gate_ops->save_context(gate_hw);
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    periph->parent_ctx = clk_periph_get_parent(hw);
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +static void clk_periph_restore_context(struct clk_hw *hw)
>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>>>>> +    const struct clk_ops *div_ops = periph->div_ops;
>>>>>>>>>>>>>>> +    struct clk_hw *div_hw = &periph->divider.hw;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    clk_periph_set_parent(hw, periph->parent_ctx);
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
>>>>>>>>>>>>>>> + div_ops->restore_context(div_hw);
>>>>>>>>>>>>>> Could you please point to where the divider's save_context()
>>>>>>>>>>>>>> happens?
>>>>>>>>>>>>>> Because I can't see it.
>>>>>>>>>>>>> Ah, I now see that there is no need to save the dividers
>>>>>>>>>>>>> context
>>>>>>>>>>>>> because
>>>>>>>>>>>>> clk itself has enough info that is needed for the context's
>>>>>>>>>>>>> restoring
>>>>>>>>>>>>> (like I pointed in the review to v6).
>>>>>>>>>>>>>
>>>>>>>>>>>>> Looks like you could also implement a new
>>>>>>>>>>>>> clk_hw_get_parent_index()
>>>>>>>>>>>>> generic helper to get the index instead of storing it
>>>>>>>>>>>>> manually.
>>>>>>>>>>>> clk_periph_get_parent basically invokes existing clk_mux_ops
>>>>>>>>>>>> get_parent() which is then saved in tegra_clk_periph.
>>>>>>>>>>>>
>>>>>>>>>>>> All existing drivers are using directly get_parent() from
>>>>>>>>>>>> clk_mux
>>>>>>>>>>>> which actually gets index from the register read.
>>>>>>>>>>>>
>>>>>>>>>>>> To have this more generic w.r.t save/restore context point of
>>>>>>>>>>>> view,
>>>>>>>>>>>> probably instead of implementing new get_parent_index helper,
>>>>>>>>>>>> I think
>>>>>>>>>>>> its better to implement save_context and restore_context to
>>>>>>>>>>>> clk_mux_ops along with creating parent_index field into
>>>>>>>>>>>> clk_mux to
>>>>>>>>>>>> cache index during set_parent.
>>>>>>>>>>>>
>>>>>>>>>>>> So we just need to invoke mux_ops save_context and
>>>>>>>>>>>> restore_context.
>>>>>>>>>>>>
>>>>>>>>>>> I hope its ok to add save/restore context to clk_mux_ops to be
>>>>>>>>>>> more
>>>>>>>>>>> generic w.r.t save/restore context rather than get_parent_index
>>>>>>>>>>> API.
>>>>>>>>>>> Please confirm if you agree.
>>>>>>>>>> Sounds like a good idea. I see that there is a 'restoring'
>>>>>>>>>> helper for
>>>>>>>>>> the generic clk_gate, seems something similar could be done
>>>>>>>>>> for the
>>>>>>>>>> clk_mux. And looks like anyway you'll need to associate the
>>>>>>>>>> parent
>>>>>>>>>> clock
>>>>>>>>>> with the hw index in order to restore the muxing.
>>>>>>>>> by 'restoring' helper for generic clk_gate, are you referring to
>>>>>>>>> clk_gate_restore_context API?
>>>>>>>> Yes.
>>>>>>>>
>>>>>>>>> clk_gate_restore_context is API that's any clk drivers can use for
>>>>>>>>> clk_gate operation restore for custom gate clk_ops.
>>>>>>>>>
>>>>>>>>> But clk-periph is directly using generic clk_mux ops from clk_mux
>>>>>>>>> so I
>>>>>>>>> think we should add .restore_context to clk_mux_ops and then
>>>>>>>>> during
>>>>>>>>> clk-periph restore need to invoke mux_ops->restore_context.
>>>>>>>> I'm not sure whether it will be good for every driver that uses
>>>>>>>> generic
>>>>>>>> clk_mux ops. Should be more flexible to have a generic helper
>>>>>>>> function
>>>>>>>> that any driver could use in order to restore the clock's parent.
>>>>>>>>
>>>>>>>> The clk-periph restoring also includes case of combining divider
>>>>>>>> and
>>>>>>>> parent restoring, so generic helper could be useful in that case
>>>>>>>> as well.
>>>>>>>>
>>>>>>>> It also looks like you could actually use the
>>>>>>>> clk_gate_restore_context()
>>>>>>>> instead of manually saving the clock's enable-state, couldn't you?
>>>>>>> ok for clk_mux, can add generic clk_mux_restore_context API rather
>>>>>>> than
>>>>>>> using restore_context in clk_ops and will invoke that during
>>>>>>> clk_periph
>>>>>>> restore.
>>>>>>>
>>> digging thru looks like for clk_periph source restore instead of
>>> clk_mux_restore_context, i can directly do clk_hw_get_parent and
>>> clk_set_parent with mux_hw as they invoke mux_ops get/set parent anyway.
>>> Will do this for periph clk mux
>>>>>>> Reg clk_gate, looks like we cant use generic
>>>>>>> clk_gate_restore_context
>>>>>>> for clk-periph as it calls enable/disable callbacks and
>>>>>>> clk_periph_enable/disable in clk-periph-gate also updated refcnt and
>>>>>>> depending on that actual enable/disable is set.
>>>>>>>
>>>>>>> During suspend, peripherals that are already enabled have their
>>>>>>> refcnt >
>>>>>>> 1, so they dont go thru enable/disable on restore if we use same
>>>>>>> enable/disable callback.
>>>>>> Looks like you could just decrement the gate's enable_refcnt on
>>>>>> save_context, wouldn't that work?
>>>>>>
>>>> gate->enable_refcnt is within clk-periph-gate which gets updated when
>>>> enable/disable callbacks get execute thru clk_core_enable/disable.
>>>> But actual enable_count used in clk_gate_restore_context is the one
>>>> which gets updated with in the clk core enable/disable functions which
>>>> invokes these callbacks. Depending on this enable_count in clk core it
>>>> invokes enable/disable.
>>>>
>>>> So, this will cause mismatch if we handle refcnt during save/restore
>>>> of tegra_clk_periph_gate_ops and also enable/disable thru this
>>>> clk_gate_restore_context is based on enable_count from clk core.
>>>>
>>>>>>> Also to align exact reset state along with CLK (like for case where
>>>>>>> CLK
>>>>>>> is enabled but peripheral might be in reset state), implemented
>>>>>>> save/restore in tegra specific tegra_clk_periph_gate_ops
>>>>>> I'm wondering whether instead of saving/restoring reset-state of
>>>>>> every
>>>>>> clock, you could simply save/restore the whole RST_DEV_x_SET
>>>>>> register.
>>>>>> Couldn't you?
>>>>> Thats what I was doing in first version of patch. But later as we
>>>>> moved to use clk_save_context and clk_restore_context, peripheral
>>>>> clk_hw RST & CLK enables happen thru its corresponding save/restore
>>>>> after source restore
>>>>
>>>> Also, to align both CLK & RST to the exact state of register, doing
>>>> save/restore in tegra_clk_periph_gate_ops and invoking this after
>>>> source restore for peripheral clock, seems cleaner to avoid any
>>>> misconfiguration b/w rst & clk settings.
>>>>
>> It looks to me that it is very wasteful to store/restore each individual
>> gate and reset state, also given that some of them are shared. I think
>> that the gates and resets should be restored separately for the
>> peripherals by a custom tegra_clk_save/restore_periph_gates/resets().
> clk_periph_fixed_disable just disables clock only without deasserting
> the corresponding peripheral.
> 
> corresponding peripheral drivers can also issue reset assert/deassert
> thru reset_control_assert/deassert.
> 
> So, we will not get the actual state of clk and rst unless we read and
> save state of reset and clock separately during save_context.
> 
> Currently patch is already using custom
> tegra_clk_periph_fixed_save/restore_context for corresponding clk_ops.
> Are you suggesting to do save and restore of complete CLK_ENB/RST_DEV
> register settings instead of individual peripheral bits? 

Yes, I'm suggesting to do a complete ungate/reset handling of the
devices in a separate function. All enabling/deassertion will be done in
a single hop, hence using 7us delay and four u32 words, which is much
nicer IMHO.

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-08-02 20:13                               ` Dmitry Osipenko
@ 2019-08-02 20:17                                 ` Dmitry Osipenko
  2019-08-02 20:32                                   ` Sowjanya Komatineni
  0 siblings, 1 reply; 67+ messages in thread
From: Dmitry Osipenko @ 2019-08-02 20:17 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree

02.08.2019 23:13, Dmitry Osipenko пишет:
> 02.08.2019 21:33, Sowjanya Komatineni пишет:
>>
>> On 8/2/19 5:38 AM, Dmitry Osipenko wrote:
>>> 02.08.2019 2:49, Sowjanya Komatineni пишет:
>>>> On 8/1/19 4:19 PM, Sowjanya Komatineni wrote:
>>>>> On 8/1/19 2:30 PM, Sowjanya Komatineni wrote:
>>>>>> On 8/1/19 1:54 PM, Dmitry Osipenko wrote:
>>>>>>> 01.08.2019 23:31, Sowjanya Komatineni пишет:
>>>>>>>> On 8/1/19 1:17 PM, Dmitry Osipenko wrote:
>>>>>>>>> 01.08.2019 22:42, Sowjanya Komatineni пишет:
>>>>>>>>>> On 8/1/19 12:00 PM, Dmitry Osipenko wrote:
>>>>>>>>>>> 01.08.2019 20:58, Sowjanya Komatineni пишет:
>>>>>>>>>>>> On 7/31/19 4:09 PM, Sowjanya Komatineni wrote:
>>>>>>>>>>>>> On 7/31/19 3:44 AM, Dmitry Osipenko wrote:
>>>>>>>>>>>>>> 31.07.2019 12:50, Dmitry Osipenko пишет:
>>>>>>>>>>>>>>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>>> This patch implements save and restore context for
>>>>>>>>>>>>>>>> peripheral
>>>>>>>>>>>>>>>> fixed
>>>>>>>>>>>>>>>> clock ops, peripheral gate clock ops, sdmmc mux clock
>>>>>>>>>>>>>>>> ops, and
>>>>>>>>>>>>>>>> peripheral clock ops.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> During system suspend, core power goes off and looses the
>>>>>>>>>>>>>>>> settings
>>>>>>>>>>>>>>>> of the Tegra CAR controller registers.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> So during suspend entry clock and reset state of
>>>>>>>>>>>>>>>> peripherals is
>>>>>>>>>>>>>>>> saved
>>>>>>>>>>>>>>>> and on resume they are restored to have clocks back to same
>>>>>>>>>>>>>>>> rate and
>>>>>>>>>>>>>>>> state as before suspend.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>>>>>>>>>>>>>>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>       drivers/clk/tegra/clk-periph-fixed.c | 33
>>>>>>>>>>>>>>>> ++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>       drivers/clk/tegra/clk-periph-gate.c  | 34
>>>>>>>>>>>>>>>> +++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>       drivers/clk/tegra/clk-periph.c       | 37
>>>>>>>>>>>>>>>> ++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>       drivers/clk/tegra/clk-sdmmc-mux.c    | 28
>>>>>>>>>>>>>>>> +++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>       drivers/clk/tegra/clk.h              | 6 ++++++
>>>>>>>>>>>>>>>>       5 files changed, 138 insertions(+)
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>> index c088e7a280df..21b24530fa00 100644
>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>> @@ -60,11 +60,44 @@
>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_recalc_rate(struct
>>>>>>>>>>>>>>>> clk_hw *hw,
>>>>>>>>>>>>>>>>           return (unsigned long)rate;
>>>>>>>>>>>>>>>>       }
>>>>>>>>>>>>>>>>       +static int tegra_clk_periph_fixed_save_context(struct
>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    fixed->enb_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>>>>>> fixed->regs->enb_reg) &
>>>>>>>>>>>>>>>> +             mask;
>>>>>>>>>>>>>>>> +    fixed->rst_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>>>>>> fixed->regs->rst_reg) &
>>>>>>>>>>>>>>>> +             mask;
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +static void tegra_clk_periph_fixed_restore_context(struct
>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    if (fixed->enb_ctx)
>>>>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>> fixed->regs->enb_set_reg);
>>>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>> fixed->regs->enb_clr_reg);
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    udelay(2);
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    if (!fixed->rst_ctx) {
>>>>>>>>>>>>>>>> +        udelay(5); /* reset propogation delay */
>>>>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>> fixed->regs->rst_reg);
>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>       static const struct clk_ops tegra_clk_periph_fixed_ops
>>>>>>>>>>>>>>>> = {
>>>>>>>>>>>>>>>>           .is_enabled = tegra_clk_periph_fixed_is_enabled,
>>>>>>>>>>>>>>>>           .enable = tegra_clk_periph_fixed_enable,
>>>>>>>>>>>>>>>>           .disable = tegra_clk_periph_fixed_disable,
>>>>>>>>>>>>>>>>           .recalc_rate = tegra_clk_periph_fixed_recalc_rate,
>>>>>>>>>>>>>>>> +    .save_context = tegra_clk_periph_fixed_save_context,
>>>>>>>>>>>>>>>> +    .restore_context =
>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_restore_context,
>>>>>>>>>>>>>>>>       };
>>>>>>>>>>>>>>>>         struct clk *tegra_clk_register_periph_fixed(const
>>>>>>>>>>>>>>>> char
>>>>>>>>>>>>>>>> *name,
>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>> index 4b31beefc9fc..6ba5b08e0787 100644
>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>> @@ -25,6 +25,8 @@ static DEFINE_SPINLOCK(periph_ref_lock);
>>>>>>>>>>>>>>>>         #define read_rst(gate) \
>>>>>>>>>>>>>>>>           readl_relaxed(gate->clk_base +
>>>>>>>>>>>>>>>> (gate->regs->rst_reg))
>>>>>>>>>>>>>>>> +#define write_rst_set(val, gate) \
>>>>>>>>>>>>>>>> +    writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>>>>>> (gate->regs->rst_set_reg))
>>>>>>>>>>>>>>>>       #define write_rst_clr(val, gate) \
>>>>>>>>>>>>>>>>           writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>>>>>> (gate->regs->rst_clr_reg))
>>>>>>>>>>>>>>>>       @@ -110,10 +112,42 @@ static void
>>>>>>>>>>>>>>>> clk_periph_disable(struct
>>>>>>>>>>>>>>>> clk_hw *hw)
>>>>>>>>>>>>>>>> spin_unlock_irqrestore(&periph_ref_lock, flags);
>>>>>>>>>>>>>>>>       }
>>>>>>>>>>>>>>>>       +static int clk_periph_gate_save_context(struct clk_hw
>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate =
>>>>>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    gate->clk_state_ctx = read_enb(gate) &
>>>>>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>>>>>> +    gate->rst_state_ctx = read_rst(gate) &
>>>>>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +static void clk_periph_gate_restore_context(struct clk_hw
>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate =
>>>>>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    if (gate->clk_state_ctx)
>>>>>>>>>>>>>>>> + write_enb_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>>>> + write_enb_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    udelay(5);
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
>>>>>>>>>>>>>>>> +        !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
>>>>>>>>>>>>>>>> +        if (gate->rst_state_ctx)
>>>>>>>>>>>>>>>> + write_rst_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>> +        else
>>>>>>>>>>>>>>>> + write_rst_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>       const struct clk_ops tegra_clk_periph_gate_ops = {
>>>>>>>>>>>>>>>>           .is_enabled = clk_periph_is_enabled,
>>>>>>>>>>>>>>>>           .enable = clk_periph_enable,
>>>>>>>>>>>>>>>>           .disable = clk_periph_disable,
>>>>>>>>>>>>>>>> +    .save_context = clk_periph_gate_save_context,
>>>>>>>>>>>>>>>> +    .restore_context = clk_periph_gate_restore_context,
>>>>>>>>>>>>>>>>       };
>>>>>>>>>>>>>>>>         struct clk *tegra_clk_register_periph_gate(const
>>>>>>>>>>>>>>>> char *name,
>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>> index 58437da25156..06fb62955768 100644
>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>> @@ -99,6 +99,37 @@ static void clk_periph_disable(struct
>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>           gate_ops->disable(gate_hw);
>>>>>>>>>>>>>>>>       }
>>>>>>>>>>>>>>>>       +static int clk_periph_save_context(struct clk_hw *hw)
>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
>>>>>>>>>>>>>>>> +        gate_ops->save_context(gate_hw);
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    periph->parent_ctx = clk_periph_get_parent(hw);
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +static void clk_periph_restore_context(struct clk_hw *hw)
>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>>>>>> +    const struct clk_ops *div_ops = periph->div_ops;
>>>>>>>>>>>>>>>> +    struct clk_hw *div_hw = &periph->divider.hw;
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    clk_periph_set_parent(hw, periph->parent_ctx);
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
>>>>>>>>>>>>>>>> + div_ops->restore_context(div_hw);
>>>>>>>>>>>>>>> Could you please point to where the divider's save_context()
>>>>>>>>>>>>>>> happens?
>>>>>>>>>>>>>>> Because I can't see it.
>>>>>>>>>>>>>> Ah, I now see that there is no need to save the dividers
>>>>>>>>>>>>>> context
>>>>>>>>>>>>>> because
>>>>>>>>>>>>>> clk itself has enough info that is needed for the context's
>>>>>>>>>>>>>> restoring
>>>>>>>>>>>>>> (like I pointed in the review to v6).
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Looks like you could also implement a new
>>>>>>>>>>>>>> clk_hw_get_parent_index()
>>>>>>>>>>>>>> generic helper to get the index instead of storing it
>>>>>>>>>>>>>> manually.
>>>>>>>>>>>>> clk_periph_get_parent basically invokes existing clk_mux_ops
>>>>>>>>>>>>> get_parent() which is then saved in tegra_clk_periph.
>>>>>>>>>>>>>
>>>>>>>>>>>>> All existing drivers are using directly get_parent() from
>>>>>>>>>>>>> clk_mux
>>>>>>>>>>>>> which actually gets index from the register read.
>>>>>>>>>>>>>
>>>>>>>>>>>>> To have this more generic w.r.t save/restore context point of
>>>>>>>>>>>>> view,
>>>>>>>>>>>>> probably instead of implementing new get_parent_index helper,
>>>>>>>>>>>>> I think
>>>>>>>>>>>>> its better to implement save_context and restore_context to
>>>>>>>>>>>>> clk_mux_ops along with creating parent_index field into
>>>>>>>>>>>>> clk_mux to
>>>>>>>>>>>>> cache index during set_parent.
>>>>>>>>>>>>>
>>>>>>>>>>>>> So we just need to invoke mux_ops save_context and
>>>>>>>>>>>>> restore_context.
>>>>>>>>>>>>>
>>>>>>>>>>>> I hope its ok to add save/restore context to clk_mux_ops to be
>>>>>>>>>>>> more
>>>>>>>>>>>> generic w.r.t save/restore context rather than get_parent_index
>>>>>>>>>>>> API.
>>>>>>>>>>>> Please confirm if you agree.
>>>>>>>>>>> Sounds like a good idea. I see that there is a 'restoring'
>>>>>>>>>>> helper for
>>>>>>>>>>> the generic clk_gate, seems something similar could be done
>>>>>>>>>>> for the
>>>>>>>>>>> clk_mux. And looks like anyway you'll need to associate the
>>>>>>>>>>> parent
>>>>>>>>>>> clock
>>>>>>>>>>> with the hw index in order to restore the muxing.
>>>>>>>>>> by 'restoring' helper for generic clk_gate, are you referring to
>>>>>>>>>> clk_gate_restore_context API?
>>>>>>>>> Yes.
>>>>>>>>>
>>>>>>>>>> clk_gate_restore_context is API that's any clk drivers can use for
>>>>>>>>>> clk_gate operation restore for custom gate clk_ops.
>>>>>>>>>>
>>>>>>>>>> But clk-periph is directly using generic clk_mux ops from clk_mux
>>>>>>>>>> so I
>>>>>>>>>> think we should add .restore_context to clk_mux_ops and then
>>>>>>>>>> during
>>>>>>>>>> clk-periph restore need to invoke mux_ops->restore_context.
>>>>>>>>> I'm not sure whether it will be good for every driver that uses
>>>>>>>>> generic
>>>>>>>>> clk_mux ops. Should be more flexible to have a generic helper
>>>>>>>>> function
>>>>>>>>> that any driver could use in order to restore the clock's parent.
>>>>>>>>>
>>>>>>>>> The clk-periph restoring also includes case of combining divider
>>>>>>>>> and
>>>>>>>>> parent restoring, so generic helper could be useful in that case
>>>>>>>>> as well.
>>>>>>>>>
>>>>>>>>> It also looks like you could actually use the
>>>>>>>>> clk_gate_restore_context()
>>>>>>>>> instead of manually saving the clock's enable-state, couldn't you?
>>>>>>>> ok for clk_mux, can add generic clk_mux_restore_context API rather
>>>>>>>> than
>>>>>>>> using restore_context in clk_ops and will invoke that during
>>>>>>>> clk_periph
>>>>>>>> restore.
>>>>>>>>
>>>> digging thru looks like for clk_periph source restore instead of
>>>> clk_mux_restore_context, i can directly do clk_hw_get_parent and
>>>> clk_set_parent with mux_hw as they invoke mux_ops get/set parent anyway.
>>>> Will do this for periph clk mux
>>>>>>>> Reg clk_gate, looks like we cant use generic
>>>>>>>> clk_gate_restore_context
>>>>>>>> for clk-periph as it calls enable/disable callbacks and
>>>>>>>> clk_periph_enable/disable in clk-periph-gate also updated refcnt and
>>>>>>>> depending on that actual enable/disable is set.
>>>>>>>>
>>>>>>>> During suspend, peripherals that are already enabled have their
>>>>>>>> refcnt >
>>>>>>>> 1, so they dont go thru enable/disable on restore if we use same
>>>>>>>> enable/disable callback.
>>>>>>> Looks like you could just decrement the gate's enable_refcnt on
>>>>>>> save_context, wouldn't that work?
>>>>>>>
>>>>> gate->enable_refcnt is within clk-periph-gate which gets updated when
>>>>> enable/disable callbacks get execute thru clk_core_enable/disable.
>>>>> But actual enable_count used in clk_gate_restore_context is the one
>>>>> which gets updated with in the clk core enable/disable functions which
>>>>> invokes these callbacks. Depending on this enable_count in clk core it
>>>>> invokes enable/disable.
>>>>>
>>>>> So, this will cause mismatch if we handle refcnt during save/restore
>>>>> of tegra_clk_periph_gate_ops and also enable/disable thru this
>>>>> clk_gate_restore_context is based on enable_count from clk core.
>>>>>
>>>>>>>> Also to align exact reset state along with CLK (like for case where
>>>>>>>> CLK
>>>>>>>> is enabled but peripheral might be in reset state), implemented
>>>>>>>> save/restore in tegra specific tegra_clk_periph_gate_ops
>>>>>>> I'm wondering whether instead of saving/restoring reset-state of
>>>>>>> every
>>>>>>> clock, you could simply save/restore the whole RST_DEV_x_SET
>>>>>>> register.
>>>>>>> Couldn't you?
>>>>>> Thats what I was doing in first version of patch. But later as we
>>>>>> moved to use clk_save_context and clk_restore_context, peripheral
>>>>>> clk_hw RST & CLK enables happen thru its corresponding save/restore
>>>>>> after source restore
>>>>>
>>>>> Also, to align both CLK & RST to the exact state of register, doing
>>>>> save/restore in tegra_clk_periph_gate_ops and invoking this after
>>>>> source restore for peripheral clock, seems cleaner to avoid any
>>>>> misconfiguration b/w rst & clk settings.
>>>>>
>>> It looks to me that it is very wasteful to store/restore each individual
>>> gate and reset state, also given that some of them are shared. I think
>>> that the gates and resets should be restored separately for the
>>> peripherals by a custom tegra_clk_save/restore_periph_gates/resets().
>> clk_periph_fixed_disable just disables clock only without deasserting
>> the corresponding peripheral.
>>
>> corresponding peripheral drivers can also issue reset assert/deassert
>> thru reset_control_assert/deassert.
>>
>> So, we will not get the actual state of clk and rst unless we read and
>> save state of reset and clock separately during save_context.
>>
>> Currently patch is already using custom
>> tegra_clk_periph_fixed_save/restore_context for corresponding clk_ops.
>> Are you suggesting to do save and restore of complete CLK_ENB/RST_DEV
>> register settings instead of individual peripheral bits? 
> 
> Yes, I'm suggesting to do a complete ungate/reset handling of the
> devices in a separate function. All enabling/deassertion will be done in
> a single hop, hence using 7us delay and four u32 words, which is much
> nicer IMHO.

Actually six words, three for CLKs and three for RSTs.

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-08-02 18:43     ` Sowjanya Komatineni
@ 2019-08-02 20:20       ` Dmitry Osipenko
  2019-08-02 20:37         ` Sowjanya Komatineni
  0 siblings, 1 reply; 67+ messages in thread
From: Dmitry Osipenko @ 2019-08-02 20:20 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree

02.08.2019 21:43, Sowjanya Komatineni пишет:
> 
> On 8/2/19 5:32 AM, Dmitry Osipenko wrote:
>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>> This patch implements save and restore context for peripheral fixed
>>> clock ops, peripheral gate clock ops, sdmmc mux clock ops, and
>>> peripheral clock ops.
>>>
>>> During system suspend, core power goes off and looses the settings
>>> of the Tegra CAR controller registers.
>>>
>>> So during suspend entry clock and reset state of peripherals is saved
>>> and on resume they are restored to have clocks back to same rate and
>>> state as before suspend.
>>>
>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>>> ---
>>>   drivers/clk/tegra/clk-periph-fixed.c | 33
>>> ++++++++++++++++++++++++++++++++
>>>   drivers/clk/tegra/clk-periph-gate.c  | 34
>>> +++++++++++++++++++++++++++++++++
>>>   drivers/clk/tegra/clk-periph.c       | 37
>>> ++++++++++++++++++++++++++++++++++++
>>>   drivers/clk/tegra/clk-sdmmc-mux.c    | 28 +++++++++++++++++++++++++++
>>>   drivers/clk/tegra/clk.h              |  6 ++++++
>>>   5 files changed, 138 insertions(+)
>>>
>>> diff --git a/drivers/clk/tegra/clk-periph-fixed.c
>>> b/drivers/clk/tegra/clk-periph-fixed.c
>>> index c088e7a280df..21b24530fa00 100644
>>> --- a/drivers/clk/tegra/clk-periph-fixed.c
>>> +++ b/drivers/clk/tegra/clk-periph-fixed.c
>>> @@ -60,11 +60,44 @@ tegra_clk_periph_fixed_recalc_rate(struct clk_hw
>>> *hw,
>>>       return (unsigned long)rate;
>>>   }
>>>   +static int tegra_clk_periph_fixed_save_context(struct clk_hw *hw)
>>> +{
>>> +    struct tegra_clk_periph_fixed *fixed =
>>> to_tegra_clk_periph_fixed(hw);
>>> +    u32 mask = 1 << (fixed->num % 32);
>> This could be BIT(fixed->num % 32).
>>
>>> +    fixed->enb_ctx = readl_relaxed(fixed->base +
>>> fixed->regs->enb_reg) &
>>> +             mask;
>>> +    fixed->rst_ctx = readl_relaxed(fixed->base +
>>> fixed->regs->rst_reg) &
>>> +             mask;
>> The enb_ctx/rst_ctx are booleans, while you assigning an integer value
>> here. You're getting away here because bool is an 32bit unsigned int,
>> but you shouldn't rely on it and always explicitly convert to a bool.
>>
>>> +    return 0;
>>> +}
>>> +
>>> +static void tegra_clk_periph_fixed_restore_context(struct clk_hw *hw)
>>> +{
>>> +    struct tegra_clk_periph_fixed *fixed =
>>> to_tegra_clk_periph_fixed(hw);
>>> +    u32 mask = 1 << (fixed->num % 32);
>>> +
>>> +    if (fixed->enb_ctx)
>>> +        writel_relaxed(mask, fixed->base + fixed->regs->enb_set_reg);
>>> +    else
>>> +        writel_relaxed(mask, fixed->base + fixed->regs->enb_clr_reg);
>>> +
>>> +    udelay(2);
>> Will be better to read out and compare the hardware's state with the
>> restored one, then bail out if the state is unchanged.
>>
>> Shouldn't it be fence_udelay()?
>>
>>> +    if (!fixed->rst_ctx) {
>>> +        udelay(5); /* reset propogation delay */
>> Why delaying is done before the writing to the reset register?
> 
> During SC7 exit, peripheral reset state is set to POR state. So some
> peripherals will already be in reset state and making sure of
> propagation delay before releasing from reset.
> 
> It should be rst_clr_reg. will fix in next rev
> 
>>
>>> +        writel_relaxed(mask, fixed->base + fixed->regs->rst_reg);
>> I'm not quite sure what's going on here, this looks wrong.
>>
>> 1. rst_reg points to RST_DEVICES_x
>> 2. Each bit of RST_DEVICES_x represents the reset-assertion state of
>> each individual device
>> 3. By writing to rst_reg, all (!) devices are deasserted, except the one
>> device which corresponds to the mask
>> 4. The reset is asserted for a single device, while !fixed->rst_ctx
>> means that it actually should be deasserted (?)
>>
>> Apparently you should use rst_set_reg / rst_clr_reg.
> Yes, It should be rst_clr_reg. will fix in next rev
>>> +    }
>> What about the case where rst_ctx=true?
> 
> ON SC7 exit, state of RST_DEV will be POR state where most peripherals
> will already be in reset state.
> 
> Few of them which are not in reset state in POR values are those that
> need to stay de-asserted across the boot states anyway.

Okay, sounds reasonable.

BTW, it would be nice if you could add a brief clarifying comment to the
code for each of the questions asked during of the review.

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-08-02 20:17                                 ` Dmitry Osipenko
@ 2019-08-02 20:32                                   ` Sowjanya Komatineni
  2019-08-02 21:15                                     ` Dmitry Osipenko
  0 siblings, 1 reply; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-08-02 20:32 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree


On 8/2/19 1:17 PM, Dmitry Osipenko wrote:
> 02.08.2019 23:13, Dmitry Osipenko пишет:
>> 02.08.2019 21:33, Sowjanya Komatineni пишет:
>>> On 8/2/19 5:38 AM, Dmitry Osipenko wrote:
>>>> 02.08.2019 2:49, Sowjanya Komatineni пишет:
>>>>> On 8/1/19 4:19 PM, Sowjanya Komatineni wrote:
>>>>>> On 8/1/19 2:30 PM, Sowjanya Komatineni wrote:
>>>>>>> On 8/1/19 1:54 PM, Dmitry Osipenko wrote:
>>>>>>>> 01.08.2019 23:31, Sowjanya Komatineni пишет:
>>>>>>>>> On 8/1/19 1:17 PM, Dmitry Osipenko wrote:
>>>>>>>>>> 01.08.2019 22:42, Sowjanya Komatineni пишет:
>>>>>>>>>>> On 8/1/19 12:00 PM, Dmitry Osipenko wrote:
>>>>>>>>>>>> 01.08.2019 20:58, Sowjanya Komatineni пишет:
>>>>>>>>>>>>> On 7/31/19 4:09 PM, Sowjanya Komatineni wrote:
>>>>>>>>>>>>>> On 7/31/19 3:44 AM, Dmitry Osipenko wrote:
>>>>>>>>>>>>>>> 31.07.2019 12:50, Dmitry Osipenko пишет:
>>>>>>>>>>>>>>>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>>>> This patch implements save and restore context for
>>>>>>>>>>>>>>>>> peripheral
>>>>>>>>>>>>>>>>> fixed
>>>>>>>>>>>>>>>>> clock ops, peripheral gate clock ops, sdmmc mux clock
>>>>>>>>>>>>>>>>> ops, and
>>>>>>>>>>>>>>>>> peripheral clock ops.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> During system suspend, core power goes off and looses the
>>>>>>>>>>>>>>>>> settings
>>>>>>>>>>>>>>>>> of the Tegra CAR controller registers.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> So during suspend entry clock and reset state of
>>>>>>>>>>>>>>>>> peripherals is
>>>>>>>>>>>>>>>>> saved
>>>>>>>>>>>>>>>>> and on resume they are restored to have clocks back to same
>>>>>>>>>>>>>>>>> rate and
>>>>>>>>>>>>>>>>> state as before suspend.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>>>>>>>>>>>>>>>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>>        drivers/clk/tegra/clk-periph-fixed.c | 33
>>>>>>>>>>>>>>>>> ++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>        drivers/clk/tegra/clk-periph-gate.c  | 34
>>>>>>>>>>>>>>>>> +++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>        drivers/clk/tegra/clk-periph.c       | 37
>>>>>>>>>>>>>>>>> ++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>        drivers/clk/tegra/clk-sdmmc-mux.c    | 28
>>>>>>>>>>>>>>>>> +++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>        drivers/clk/tegra/clk.h              | 6 ++++++
>>>>>>>>>>>>>>>>>        5 files changed, 138 insertions(+)
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>> index c088e7a280df..21b24530fa00 100644
>>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>> @@ -60,11 +60,44 @@
>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_recalc_rate(struct
>>>>>>>>>>>>>>>>> clk_hw *hw,
>>>>>>>>>>>>>>>>>            return (unsigned long)rate;
>>>>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>>>>>        +static int tegra_clk_periph_fixed_save_context(struct
>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    fixed->enb_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>>>>>>> fixed->regs->enb_reg) &
>>>>>>>>>>>>>>>>> +             mask;
>>>>>>>>>>>>>>>>> +    fixed->rst_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>>>>>>> fixed->regs->rst_reg) &
>>>>>>>>>>>>>>>>> +             mask;
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +static void tegra_clk_periph_fixed_restore_context(struct
>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    if (fixed->enb_ctx)
>>>>>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>>> fixed->regs->enb_set_reg);
>>>>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>>> fixed->regs->enb_clr_reg);
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    udelay(2);
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    if (!fixed->rst_ctx) {
>>>>>>>>>>>>>>>>> +        udelay(5); /* reset propogation delay */
>>>>>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>>> fixed->regs->rst_reg);
>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>        static const struct clk_ops tegra_clk_periph_fixed_ops
>>>>>>>>>>>>>>>>> = {
>>>>>>>>>>>>>>>>>            .is_enabled = tegra_clk_periph_fixed_is_enabled,
>>>>>>>>>>>>>>>>>            .enable = tegra_clk_periph_fixed_enable,
>>>>>>>>>>>>>>>>>            .disable = tegra_clk_periph_fixed_disable,
>>>>>>>>>>>>>>>>>            .recalc_rate = tegra_clk_periph_fixed_recalc_rate,
>>>>>>>>>>>>>>>>> +    .save_context = tegra_clk_periph_fixed_save_context,
>>>>>>>>>>>>>>>>> +    .restore_context =
>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_restore_context,
>>>>>>>>>>>>>>>>>        };
>>>>>>>>>>>>>>>>>          struct clk *tegra_clk_register_periph_fixed(const
>>>>>>>>>>>>>>>>> char
>>>>>>>>>>>>>>>>> *name,
>>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>> index 4b31beefc9fc..6ba5b08e0787 100644
>>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>> @@ -25,6 +25,8 @@ static DEFINE_SPINLOCK(periph_ref_lock);
>>>>>>>>>>>>>>>>>          #define read_rst(gate) \
>>>>>>>>>>>>>>>>>            readl_relaxed(gate->clk_base +
>>>>>>>>>>>>>>>>> (gate->regs->rst_reg))
>>>>>>>>>>>>>>>>> +#define write_rst_set(val, gate) \
>>>>>>>>>>>>>>>>> +    writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>>>>>>> (gate->regs->rst_set_reg))
>>>>>>>>>>>>>>>>>        #define write_rst_clr(val, gate) \
>>>>>>>>>>>>>>>>>            writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>>>>>>> (gate->regs->rst_clr_reg))
>>>>>>>>>>>>>>>>>        @@ -110,10 +112,42 @@ static void
>>>>>>>>>>>>>>>>> clk_periph_disable(struct
>>>>>>>>>>>>>>>>> clk_hw *hw)
>>>>>>>>>>>>>>>>> spin_unlock_irqrestore(&periph_ref_lock, flags);
>>>>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>>>>>        +static int clk_periph_gate_save_context(struct clk_hw
>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate =
>>>>>>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    gate->clk_state_ctx = read_enb(gate) &
>>>>>>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>>>>>>> +    gate->rst_state_ctx = read_rst(gate) &
>>>>>>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +static void clk_periph_gate_restore_context(struct clk_hw
>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate =
>>>>>>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    if (gate->clk_state_ctx)
>>>>>>>>>>>>>>>>> + write_enb_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>>>>> + write_enb_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    udelay(5);
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
>>>>>>>>>>>>>>>>> +        !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
>>>>>>>>>>>>>>>>> +        if (gate->rst_state_ctx)
>>>>>>>>>>>>>>>>> + write_rst_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>> +        else
>>>>>>>>>>>>>>>>> + write_rst_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>        const struct clk_ops tegra_clk_periph_gate_ops = {
>>>>>>>>>>>>>>>>>            .is_enabled = clk_periph_is_enabled,
>>>>>>>>>>>>>>>>>            .enable = clk_periph_enable,
>>>>>>>>>>>>>>>>>            .disable = clk_periph_disable,
>>>>>>>>>>>>>>>>> +    .save_context = clk_periph_gate_save_context,
>>>>>>>>>>>>>>>>> +    .restore_context = clk_periph_gate_restore_context,
>>>>>>>>>>>>>>>>>        };
>>>>>>>>>>>>>>>>>          struct clk *tegra_clk_register_periph_gate(const
>>>>>>>>>>>>>>>>> char *name,
>>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>> index 58437da25156..06fb62955768 100644
>>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>> @@ -99,6 +99,37 @@ static void clk_periph_disable(struct
>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>            gate_ops->disable(gate_hw);
>>>>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>>>>>        +static int clk_periph_save_context(struct clk_hw *hw)
>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
>>>>>>>>>>>>>>>>> +        gate_ops->save_context(gate_hw);
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    periph->parent_ctx = clk_periph_get_parent(hw);
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +static void clk_periph_restore_context(struct clk_hw *hw)
>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>>>>>>> +    const struct clk_ops *div_ops = periph->div_ops;
>>>>>>>>>>>>>>>>> +    struct clk_hw *div_hw = &periph->divider.hw;
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    clk_periph_set_parent(hw, periph->parent_ctx);
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
>>>>>>>>>>>>>>>>> + div_ops->restore_context(div_hw);
>>>>>>>>>>>>>>>> Could you please point to where the divider's save_context()
>>>>>>>>>>>>>>>> happens?
>>>>>>>>>>>>>>>> Because I can't see it.
>>>>>>>>>>>>>>> Ah, I now see that there is no need to save the dividers
>>>>>>>>>>>>>>> context
>>>>>>>>>>>>>>> because
>>>>>>>>>>>>>>> clk itself has enough info that is needed for the context's
>>>>>>>>>>>>>>> restoring
>>>>>>>>>>>>>>> (like I pointed in the review to v6).
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Looks like you could also implement a new
>>>>>>>>>>>>>>> clk_hw_get_parent_index()
>>>>>>>>>>>>>>> generic helper to get the index instead of storing it
>>>>>>>>>>>>>>> manually.
>>>>>>>>>>>>>> clk_periph_get_parent basically invokes existing clk_mux_ops
>>>>>>>>>>>>>> get_parent() which is then saved in tegra_clk_periph.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> All existing drivers are using directly get_parent() from
>>>>>>>>>>>>>> clk_mux
>>>>>>>>>>>>>> which actually gets index from the register read.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> To have this more generic w.r.t save/restore context point of
>>>>>>>>>>>>>> view,
>>>>>>>>>>>>>> probably instead of implementing new get_parent_index helper,
>>>>>>>>>>>>>> I think
>>>>>>>>>>>>>> its better to implement save_context and restore_context to
>>>>>>>>>>>>>> clk_mux_ops along with creating parent_index field into
>>>>>>>>>>>>>> clk_mux to
>>>>>>>>>>>>>> cache index during set_parent.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> So we just need to invoke mux_ops save_context and
>>>>>>>>>>>>>> restore_context.
>>>>>>>>>>>>>>
>>>>>>>>>>>>> I hope its ok to add save/restore context to clk_mux_ops to be
>>>>>>>>>>>>> more
>>>>>>>>>>>>> generic w.r.t save/restore context rather than get_parent_index
>>>>>>>>>>>>> API.
>>>>>>>>>>>>> Please confirm if you agree.
>>>>>>>>>>>> Sounds like a good idea. I see that there is a 'restoring'
>>>>>>>>>>>> helper for
>>>>>>>>>>>> the generic clk_gate, seems something similar could be done
>>>>>>>>>>>> for the
>>>>>>>>>>>> clk_mux. And looks like anyway you'll need to associate the
>>>>>>>>>>>> parent
>>>>>>>>>>>> clock
>>>>>>>>>>>> with the hw index in order to restore the muxing.
>>>>>>>>>>> by 'restoring' helper for generic clk_gate, are you referring to
>>>>>>>>>>> clk_gate_restore_context API?
>>>>>>>>>> Yes.
>>>>>>>>>>
>>>>>>>>>>> clk_gate_restore_context is API that's any clk drivers can use for
>>>>>>>>>>> clk_gate operation restore for custom gate clk_ops.
>>>>>>>>>>>
>>>>>>>>>>> But clk-periph is directly using generic clk_mux ops from clk_mux
>>>>>>>>>>> so I
>>>>>>>>>>> think we should add .restore_context to clk_mux_ops and then
>>>>>>>>>>> during
>>>>>>>>>>> clk-periph restore need to invoke mux_ops->restore_context.
>>>>>>>>>> I'm not sure whether it will be good for every driver that uses
>>>>>>>>>> generic
>>>>>>>>>> clk_mux ops. Should be more flexible to have a generic helper
>>>>>>>>>> function
>>>>>>>>>> that any driver could use in order to restore the clock's parent.
>>>>>>>>>>
>>>>>>>>>> The clk-periph restoring also includes case of combining divider
>>>>>>>>>> and
>>>>>>>>>> parent restoring, so generic helper could be useful in that case
>>>>>>>>>> as well.
>>>>>>>>>>
>>>>>>>>>> It also looks like you could actually use the
>>>>>>>>>> clk_gate_restore_context()
>>>>>>>>>> instead of manually saving the clock's enable-state, couldn't you?
>>>>>>>>> ok for clk_mux, can add generic clk_mux_restore_context API rather
>>>>>>>>> than
>>>>>>>>> using restore_context in clk_ops and will invoke that during
>>>>>>>>> clk_periph
>>>>>>>>> restore.
>>>>>>>>>
>>>>> digging thru looks like for clk_periph source restore instead of
>>>>> clk_mux_restore_context, i can directly do clk_hw_get_parent and
>>>>> clk_set_parent with mux_hw as they invoke mux_ops get/set parent anyway.
>>>>> Will do this for periph clk mux
>>>>>>>>> Reg clk_gate, looks like we cant use generic
>>>>>>>>> clk_gate_restore_context
>>>>>>>>> for clk-periph as it calls enable/disable callbacks and
>>>>>>>>> clk_periph_enable/disable in clk-periph-gate also updated refcnt and
>>>>>>>>> depending on that actual enable/disable is set.
>>>>>>>>>
>>>>>>>>> During suspend, peripherals that are already enabled have their
>>>>>>>>> refcnt >
>>>>>>>>> 1, so they dont go thru enable/disable on restore if we use same
>>>>>>>>> enable/disable callback.
>>>>>>>> Looks like you could just decrement the gate's enable_refcnt on
>>>>>>>> save_context, wouldn't that work?
>>>>>>>>
>>>>>> gate->enable_refcnt is within clk-periph-gate which gets updated when
>>>>>> enable/disable callbacks get execute thru clk_core_enable/disable.
>>>>>> But actual enable_count used in clk_gate_restore_context is the one
>>>>>> which gets updated with in the clk core enable/disable functions which
>>>>>> invokes these callbacks. Depending on this enable_count in clk core it
>>>>>> invokes enable/disable.
>>>>>>
>>>>>> So, this will cause mismatch if we handle refcnt during save/restore
>>>>>> of tegra_clk_periph_gate_ops and also enable/disable thru this
>>>>>> clk_gate_restore_context is based on enable_count from clk core.
>>>>>>
>>>>>>>>> Also to align exact reset state along with CLK (like for case where
>>>>>>>>> CLK
>>>>>>>>> is enabled but peripheral might be in reset state), implemented
>>>>>>>>> save/restore in tegra specific tegra_clk_periph_gate_ops
>>>>>>>> I'm wondering whether instead of saving/restoring reset-state of
>>>>>>>> every
>>>>>>>> clock, you could simply save/restore the whole RST_DEV_x_SET
>>>>>>>> register.
>>>>>>>> Couldn't you?
>>>>>>> Thats what I was doing in first version of patch. But later as we
>>>>>>> moved to use clk_save_context and clk_restore_context, peripheral
>>>>>>> clk_hw RST & CLK enables happen thru its corresponding save/restore
>>>>>>> after source restore
>>>>>> Also, to align both CLK & RST to the exact state of register, doing
>>>>>> save/restore in tegra_clk_periph_gate_ops and invoking this after
>>>>>> source restore for peripheral clock, seems cleaner to avoid any
>>>>>> misconfiguration b/w rst & clk settings.
>>>>>>
>>>> It looks to me that it is very wasteful to store/restore each individual
>>>> gate and reset state, also given that some of them are shared. I think
>>>> that the gates and resets should be restored separately for the
>>>> peripherals by a custom tegra_clk_save/restore_periph_gates/resets().
>>> clk_periph_fixed_disable just disables clock only without deasserting
>>> the corresponding peripheral.
>>>
>>> corresponding peripheral drivers can also issue reset assert/deassert
>>> thru reset_control_assert/deassert.
>>>
>>> So, we will not get the actual state of clk and rst unless we read and
>>> save state of reset and clock separately during save_context.
>>>
>>> Currently patch is already using custom
>>> tegra_clk_periph_fixed_save/restore_context for corresponding clk_ops.
>>> Are you suggesting to do save and restore of complete CLK_ENB/RST_DEV
>>> register settings instead of individual peripheral bits?
>> Yes, I'm suggesting to do a complete ungate/reset handling of the
>> devices in a separate function. All enabling/deassertion will be done in
>> a single hop, hence using 7us delay and four u32 words, which is much
>> nicer IMHO.
> Actually six words, three for CLKs and three for RSTs.

OK, So with separate function doing complete register save/restore for 
clk & rst, we can't do this thru clk_ops save/restore as clk_ops 
save_restore happens per peripheral wise. So if we decide to do this, 
then this should be invoked in clk-tegra210 driver suspend/resume.

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-08-02 20:20       ` Dmitry Osipenko
@ 2019-08-02 20:37         ` Sowjanya Komatineni
  0 siblings, 0 replies; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-08-02 20:37 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree


On 8/2/19 1:20 PM, Dmitry Osipenko wrote:
> 02.08.2019 21:43, Sowjanya Komatineni пишет:
>> On 8/2/19 5:32 AM, Dmitry Osipenko wrote:
>>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>>> This patch implements save and restore context for peripheral fixed
>>>> clock ops, peripheral gate clock ops, sdmmc mux clock ops, and
>>>> peripheral clock ops.
>>>>
>>>> During system suspend, core power goes off and looses the settings
>>>> of the Tegra CAR controller registers.
>>>>
>>>> So during suspend entry clock and reset state of peripherals is saved
>>>> and on resume they are restored to have clocks back to same rate and
>>>> state as before suspend.
>>>>
>>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>>> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
>>>> ---
>>>>    drivers/clk/tegra/clk-periph-fixed.c | 33
>>>> ++++++++++++++++++++++++++++++++
>>>>    drivers/clk/tegra/clk-periph-gate.c  | 34
>>>> +++++++++++++++++++++++++++++++++
>>>>    drivers/clk/tegra/clk-periph.c       | 37
>>>> ++++++++++++++++++++++++++++++++++++
>>>>    drivers/clk/tegra/clk-sdmmc-mux.c    | 28 +++++++++++++++++++++++++++
>>>>    drivers/clk/tegra/clk.h              |  6 ++++++
>>>>    5 files changed, 138 insertions(+)
>>>>
>>>> diff --git a/drivers/clk/tegra/clk-periph-fixed.c
>>>> b/drivers/clk/tegra/clk-periph-fixed.c
>>>> index c088e7a280df..21b24530fa00 100644
>>>> --- a/drivers/clk/tegra/clk-periph-fixed.c
>>>> +++ b/drivers/clk/tegra/clk-periph-fixed.c
>>>> @@ -60,11 +60,44 @@ tegra_clk_periph_fixed_recalc_rate(struct clk_hw
>>>> *hw,
>>>>        return (unsigned long)rate;
>>>>    }
>>>>    +static int tegra_clk_periph_fixed_save_context(struct clk_hw *hw)
>>>> +{
>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>> to_tegra_clk_periph_fixed(hw);
>>>> +    u32 mask = 1 << (fixed->num % 32);
>>> This could be BIT(fixed->num % 32).
>>>
>>>> +    fixed->enb_ctx = readl_relaxed(fixed->base +
>>>> fixed->regs->enb_reg) &
>>>> +             mask;
>>>> +    fixed->rst_ctx = readl_relaxed(fixed->base +
>>>> fixed->regs->rst_reg) &
>>>> +             mask;
>>> The enb_ctx/rst_ctx are booleans, while you assigning an integer value
>>> here. You're getting away here because bool is an 32bit unsigned int,
>>> but you shouldn't rely on it and always explicitly convert to a bool.
>>>
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static void tegra_clk_periph_fixed_restore_context(struct clk_hw *hw)
>>>> +{
>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>> to_tegra_clk_periph_fixed(hw);
>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>> +
>>>> +    if (fixed->enb_ctx)
>>>> +        writel_relaxed(mask, fixed->base + fixed->regs->enb_set_reg);
>>>> +    else
>>>> +        writel_relaxed(mask, fixed->base + fixed->regs->enb_clr_reg);
>>>> +
>>>> +    udelay(2);
>>> Will be better to read out and compare the hardware's state with the
>>> restored one, then bail out if the state is unchanged.
>>>
>>> Shouldn't it be fence_udelay()?
>>>
>>>> +    if (!fixed->rst_ctx) {
>>>> +        udelay(5); /* reset propogation delay */
>>> Why delaying is done before the writing to the reset register?
>> During SC7 exit, peripheral reset state is set to POR state. So some
>> peripherals will already be in reset state and making sure of
>> propagation delay before releasing from reset.
>>
>> It should be rst_clr_reg. will fix in next rev
>>
>>>> +        writel_relaxed(mask, fixed->base + fixed->regs->rst_reg);
>>> I'm not quite sure what's going on here, this looks wrong.
>>>
>>> 1. rst_reg points to RST_DEVICES_x
>>> 2. Each bit of RST_DEVICES_x represents the reset-assertion state of
>>> each individual device
>>> 3. By writing to rst_reg, all (!) devices are deasserted, except the one
>>> device which corresponds to the mask
>>> 4. The reset is asserted for a single device, while !fixed->rst_ctx
>>> means that it actually should be deasserted (?)
>>>
>>> Apparently you should use rst_set_reg / rst_clr_reg.
>> Yes, It should be rst_clr_reg. will fix in next rev
>>>> +    }
>>> What about the case where rst_ctx=true?
>> ON SC7 exit, state of RST_DEV will be POR state where most peripherals
>> will already be in reset state.
>>
>> Few of them which are not in reset state in POR values are those that
>> need to stay de-asserted across the boot states anyway.
> Okay, sounds reasonable.
>
> BTW, it would be nice if you could add a brief clarifying comment to the
> code for each of the questions asked during of the review.
OK, Will add comments in code ...

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-08-02 20:32                                   ` Sowjanya Komatineni
@ 2019-08-02 21:15                                     ` Dmitry Osipenko
  2019-08-02 21:18                                       ` Sowjanya Komatineni
  2019-08-02 23:51                                       ` Sowjanya Komatineni
  0 siblings, 2 replies; 67+ messages in thread
From: Dmitry Osipenko @ 2019-08-02 21:15 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree

02.08.2019 23:32, Sowjanya Komatineni пишет:
> 
> On 8/2/19 1:17 PM, Dmitry Osipenko wrote:
>> 02.08.2019 23:13, Dmitry Osipenko пишет:
>>> 02.08.2019 21:33, Sowjanya Komatineni пишет:
>>>> On 8/2/19 5:38 AM, Dmitry Osipenko wrote:
>>>>> 02.08.2019 2:49, Sowjanya Komatineni пишет:
>>>>>> On 8/1/19 4:19 PM, Sowjanya Komatineni wrote:
>>>>>>> On 8/1/19 2:30 PM, Sowjanya Komatineni wrote:
>>>>>>>> On 8/1/19 1:54 PM, Dmitry Osipenko wrote:
>>>>>>>>> 01.08.2019 23:31, Sowjanya Komatineni пишет:
>>>>>>>>>> On 8/1/19 1:17 PM, Dmitry Osipenko wrote:
>>>>>>>>>>> 01.08.2019 22:42, Sowjanya Komatineni пишет:
>>>>>>>>>>>> On 8/1/19 12:00 PM, Dmitry Osipenko wrote:
>>>>>>>>>>>>> 01.08.2019 20:58, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>> On 7/31/19 4:09 PM, Sowjanya Komatineni wrote:
>>>>>>>>>>>>>>> On 7/31/19 3:44 AM, Dmitry Osipenko wrote:
>>>>>>>>>>>>>>>> 31.07.2019 12:50, Dmitry Osipenko пишет:
>>>>>>>>>>>>>>>>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>>>>> This patch implements save and restore context for
>>>>>>>>>>>>>>>>>> peripheral
>>>>>>>>>>>>>>>>>> fixed
>>>>>>>>>>>>>>>>>> clock ops, peripheral gate clock ops, sdmmc mux clock
>>>>>>>>>>>>>>>>>> ops, and
>>>>>>>>>>>>>>>>>> peripheral clock ops.
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> During system suspend, core power goes off and looses the
>>>>>>>>>>>>>>>>>> settings
>>>>>>>>>>>>>>>>>> of the Tegra CAR controller registers.
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> So during suspend entry clock and reset state of
>>>>>>>>>>>>>>>>>> peripherals is
>>>>>>>>>>>>>>>>>> saved
>>>>>>>>>>>>>>>>>> and on resume they are restored to have clocks back to
>>>>>>>>>>>>>>>>>> same
>>>>>>>>>>>>>>>>>> rate and
>>>>>>>>>>>>>>>>>> state as before suspend.
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>>>>>>>>>>>>>>>>> Signed-off-by: Sowjanya Komatineni
>>>>>>>>>>>>>>>>>> <skomatineni@nvidia.com>
>>>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>>>        drivers/clk/tegra/clk-periph-fixed.c | 33
>>>>>>>>>>>>>>>>>> ++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>        drivers/clk/tegra/clk-periph-gate.c  | 34
>>>>>>>>>>>>>>>>>> +++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>        drivers/clk/tegra/clk-periph.c       | 37
>>>>>>>>>>>>>>>>>> ++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>        drivers/clk/tegra/clk-sdmmc-mux.c    | 28
>>>>>>>>>>>>>>>>>> +++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>        drivers/clk/tegra/clk.h              | 6 ++++++
>>>>>>>>>>>>>>>>>>        5 files changed, 138 insertions(+)
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>> index c088e7a280df..21b24530fa00 100644
>>>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>> @@ -60,11 +60,44 @@
>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_recalc_rate(struct
>>>>>>>>>>>>>>>>>> clk_hw *hw,
>>>>>>>>>>>>>>>>>>            return (unsigned long)rate;
>>>>>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>>>>>>        +static int
>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_save_context(struct
>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +    fixed->enb_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>>>>>>>> fixed->regs->enb_reg) &
>>>>>>>>>>>>>>>>>> +             mask;
>>>>>>>>>>>>>>>>>> +    fixed->rst_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>>>>>>>> fixed->regs->rst_reg) &
>>>>>>>>>>>>>>>>>> +             mask;
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +static void
>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_restore_context(struct
>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +    if (fixed->enb_ctx)
>>>>>>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>>>> fixed->regs->enb_set_reg);
>>>>>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>>>> fixed->regs->enb_clr_reg);
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +    udelay(2);
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +    if (!fixed->rst_ctx) {
>>>>>>>>>>>>>>>>>> +        udelay(5); /* reset propogation delay */
>>>>>>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>>>> fixed->regs->rst_reg);
>>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>        static const struct clk_ops
>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_ops
>>>>>>>>>>>>>>>>>> = {
>>>>>>>>>>>>>>>>>>            .is_enabled =
>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_is_enabled,
>>>>>>>>>>>>>>>>>>            .enable = tegra_clk_periph_fixed_enable,
>>>>>>>>>>>>>>>>>>            .disable = tegra_clk_periph_fixed_disable,
>>>>>>>>>>>>>>>>>>            .recalc_rate =
>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_recalc_rate,
>>>>>>>>>>>>>>>>>> +    .save_context = tegra_clk_periph_fixed_save_context,
>>>>>>>>>>>>>>>>>> +    .restore_context =
>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_restore_context,
>>>>>>>>>>>>>>>>>>        };
>>>>>>>>>>>>>>>>>>          struct clk
>>>>>>>>>>>>>>>>>> *tegra_clk_register_periph_fixed(const
>>>>>>>>>>>>>>>>>> char
>>>>>>>>>>>>>>>>>> *name,
>>>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>> index 4b31beefc9fc..6ba5b08e0787 100644
>>>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>> @@ -25,6 +25,8 @@ static
>>>>>>>>>>>>>>>>>> DEFINE_SPINLOCK(periph_ref_lock);
>>>>>>>>>>>>>>>>>>          #define read_rst(gate) \
>>>>>>>>>>>>>>>>>>            readl_relaxed(gate->clk_base +
>>>>>>>>>>>>>>>>>> (gate->regs->rst_reg))
>>>>>>>>>>>>>>>>>> +#define write_rst_set(val, gate) \
>>>>>>>>>>>>>>>>>> +    writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>>>>>>>> (gate->regs->rst_set_reg))
>>>>>>>>>>>>>>>>>>        #define write_rst_clr(val, gate) \
>>>>>>>>>>>>>>>>>>            writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>>>>>>>> (gate->regs->rst_clr_reg))
>>>>>>>>>>>>>>>>>>        @@ -110,10 +112,42 @@ static void
>>>>>>>>>>>>>>>>>> clk_periph_disable(struct
>>>>>>>>>>>>>>>>>> clk_hw *hw)
>>>>>>>>>>>>>>>>>> spin_unlock_irqrestore(&periph_ref_lock, flags);
>>>>>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>>>>>>        +static int clk_periph_gate_save_context(struct
>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate =
>>>>>>>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +    gate->clk_state_ctx = read_enb(gate) &
>>>>>>>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>>>>>>>> +    gate->rst_state_ctx = read_rst(gate) &
>>>>>>>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +static void clk_periph_gate_restore_context(struct
>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate =
>>>>>>>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +    if (gate->clk_state_ctx)
>>>>>>>>>>>>>>>>>> + write_enb_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>>>>>> + write_enb_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +    udelay(5);
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +    if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
>>>>>>>>>>>>>>>>>> +        !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
>>>>>>>>>>>>>>>>>> +        if (gate->rst_state_ctx)
>>>>>>>>>>>>>>>>>> + write_rst_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>> +        else
>>>>>>>>>>>>>>>>>> + write_rst_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>        const struct clk_ops tegra_clk_periph_gate_ops = {
>>>>>>>>>>>>>>>>>>            .is_enabled = clk_periph_is_enabled,
>>>>>>>>>>>>>>>>>>            .enable = clk_periph_enable,
>>>>>>>>>>>>>>>>>>            .disable = clk_periph_disable,
>>>>>>>>>>>>>>>>>> +    .save_context = clk_periph_gate_save_context,
>>>>>>>>>>>>>>>>>> +    .restore_context = clk_periph_gate_restore_context,
>>>>>>>>>>>>>>>>>>        };
>>>>>>>>>>>>>>>>>>          struct clk *tegra_clk_register_periph_gate(const
>>>>>>>>>>>>>>>>>> char *name,
>>>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>> index 58437da25156..06fb62955768 100644
>>>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>> @@ -99,6 +99,37 @@ static void clk_periph_disable(struct
>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>            gate_ops->disable(gate_hw);
>>>>>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>>>>>>        +static int clk_periph_save_context(struct
>>>>>>>>>>>>>>>>>> clk_hw *hw)
>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>>>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
>>>>>>>>>>>>>>>>>> +        gate_ops->save_context(gate_hw);
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +    periph->parent_ctx = clk_periph_get_parent(hw);
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +static void clk_periph_restore_context(struct clk_hw
>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>>>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>>>>>>>> +    const struct clk_ops *div_ops = periph->div_ops;
>>>>>>>>>>>>>>>>>> +    struct clk_hw *div_hw = &periph->divider.hw;
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +    clk_periph_set_parent(hw, periph->parent_ctx);
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
>>>>>>>>>>>>>>>>>> + div_ops->restore_context(div_hw);
>>>>>>>>>>>>>>>>> Could you please point to where the divider's
>>>>>>>>>>>>>>>>> save_context()
>>>>>>>>>>>>>>>>> happens?
>>>>>>>>>>>>>>>>> Because I can't see it.
>>>>>>>>>>>>>>>> Ah, I now see that there is no need to save the dividers
>>>>>>>>>>>>>>>> context
>>>>>>>>>>>>>>>> because
>>>>>>>>>>>>>>>> clk itself has enough info that is needed for the context's
>>>>>>>>>>>>>>>> restoring
>>>>>>>>>>>>>>>> (like I pointed in the review to v6).
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Looks like you could also implement a new
>>>>>>>>>>>>>>>> clk_hw_get_parent_index()
>>>>>>>>>>>>>>>> generic helper to get the index instead of storing it
>>>>>>>>>>>>>>>> manually.
>>>>>>>>>>>>>>> clk_periph_get_parent basically invokes existing clk_mux_ops
>>>>>>>>>>>>>>> get_parent() which is then saved in tegra_clk_periph.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> All existing drivers are using directly get_parent() from
>>>>>>>>>>>>>>> clk_mux
>>>>>>>>>>>>>>> which actually gets index from the register read.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> To have this more generic w.r.t save/restore context
>>>>>>>>>>>>>>> point of
>>>>>>>>>>>>>>> view,
>>>>>>>>>>>>>>> probably instead of implementing new get_parent_index
>>>>>>>>>>>>>>> helper,
>>>>>>>>>>>>>>> I think
>>>>>>>>>>>>>>> its better to implement save_context and restore_context to
>>>>>>>>>>>>>>> clk_mux_ops along with creating parent_index field into
>>>>>>>>>>>>>>> clk_mux to
>>>>>>>>>>>>>>> cache index during set_parent.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> So we just need to invoke mux_ops save_context and
>>>>>>>>>>>>>>> restore_context.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>> I hope its ok to add save/restore context to clk_mux_ops
>>>>>>>>>>>>>> to be
>>>>>>>>>>>>>> more
>>>>>>>>>>>>>> generic w.r.t save/restore context rather than
>>>>>>>>>>>>>> get_parent_index
>>>>>>>>>>>>>> API.
>>>>>>>>>>>>>> Please confirm if you agree.
>>>>>>>>>>>>> Sounds like a good idea. I see that there is a 'restoring'
>>>>>>>>>>>>> helper for
>>>>>>>>>>>>> the generic clk_gate, seems something similar could be done
>>>>>>>>>>>>> for the
>>>>>>>>>>>>> clk_mux. And looks like anyway you'll need to associate the
>>>>>>>>>>>>> parent
>>>>>>>>>>>>> clock
>>>>>>>>>>>>> with the hw index in order to restore the muxing.
>>>>>>>>>>>> by 'restoring' helper for generic clk_gate, are you
>>>>>>>>>>>> referring to
>>>>>>>>>>>> clk_gate_restore_context API?
>>>>>>>>>>> Yes.
>>>>>>>>>>>
>>>>>>>>>>>> clk_gate_restore_context is API that's any clk drivers can
>>>>>>>>>>>> use for
>>>>>>>>>>>> clk_gate operation restore for custom gate clk_ops.
>>>>>>>>>>>>
>>>>>>>>>>>> But clk-periph is directly using generic clk_mux ops from
>>>>>>>>>>>> clk_mux
>>>>>>>>>>>> so I
>>>>>>>>>>>> think we should add .restore_context to clk_mux_ops and then
>>>>>>>>>>>> during
>>>>>>>>>>>> clk-periph restore need to invoke mux_ops->restore_context.
>>>>>>>>>>> I'm not sure whether it will be good for every driver that uses
>>>>>>>>>>> generic
>>>>>>>>>>> clk_mux ops. Should be more flexible to have a generic helper
>>>>>>>>>>> function
>>>>>>>>>>> that any driver could use in order to restore the clock's
>>>>>>>>>>> parent.
>>>>>>>>>>>
>>>>>>>>>>> The clk-periph restoring also includes case of combining divider
>>>>>>>>>>> and
>>>>>>>>>>> parent restoring, so generic helper could be useful in that case
>>>>>>>>>>> as well.
>>>>>>>>>>>
>>>>>>>>>>> It also looks like you could actually use the
>>>>>>>>>>> clk_gate_restore_context()
>>>>>>>>>>> instead of manually saving the clock's enable-state, couldn't
>>>>>>>>>>> you?
>>>>>>>>>> ok for clk_mux, can add generic clk_mux_restore_context API
>>>>>>>>>> rather
>>>>>>>>>> than
>>>>>>>>>> using restore_context in clk_ops and will invoke that during
>>>>>>>>>> clk_periph
>>>>>>>>>> restore.
>>>>>>>>>>
>>>>>> digging thru looks like for clk_periph source restore instead of
>>>>>> clk_mux_restore_context, i can directly do clk_hw_get_parent and
>>>>>> clk_set_parent with mux_hw as they invoke mux_ops get/set parent
>>>>>> anyway.
>>>>>> Will do this for periph clk mux
>>>>>>>>>> Reg clk_gate, looks like we cant use generic
>>>>>>>>>> clk_gate_restore_context
>>>>>>>>>> for clk-periph as it calls enable/disable callbacks and
>>>>>>>>>> clk_periph_enable/disable in clk-periph-gate also updated
>>>>>>>>>> refcnt and
>>>>>>>>>> depending on that actual enable/disable is set.
>>>>>>>>>>
>>>>>>>>>> During suspend, peripherals that are already enabled have their
>>>>>>>>>> refcnt >
>>>>>>>>>> 1, so they dont go thru enable/disable on restore if we use same
>>>>>>>>>> enable/disable callback.
>>>>>>>>> Looks like you could just decrement the gate's enable_refcnt on
>>>>>>>>> save_context, wouldn't that work?
>>>>>>>>>
>>>>>>> gate->enable_refcnt is within clk-periph-gate which gets updated
>>>>>>> when
>>>>>>> enable/disable callbacks get execute thru clk_core_enable/disable.
>>>>>>> But actual enable_count used in clk_gate_restore_context is the one
>>>>>>> which gets updated with in the clk core enable/disable functions
>>>>>>> which
>>>>>>> invokes these callbacks. Depending on this enable_count in clk
>>>>>>> core it
>>>>>>> invokes enable/disable.
>>>>>>>
>>>>>>> So, this will cause mismatch if we handle refcnt during save/restore
>>>>>>> of tegra_clk_periph_gate_ops and also enable/disable thru this
>>>>>>> clk_gate_restore_context is based on enable_count from clk core.
>>>>>>>
>>>>>>>>>> Also to align exact reset state along with CLK (like for case
>>>>>>>>>> where
>>>>>>>>>> CLK
>>>>>>>>>> is enabled but peripheral might be in reset state), implemented
>>>>>>>>>> save/restore in tegra specific tegra_clk_periph_gate_ops
>>>>>>>>> I'm wondering whether instead of saving/restoring reset-state of
>>>>>>>>> every
>>>>>>>>> clock, you could simply save/restore the whole RST_DEV_x_SET
>>>>>>>>> register.
>>>>>>>>> Couldn't you?
>>>>>>>> Thats what I was doing in first version of patch. But later as we
>>>>>>>> moved to use clk_save_context and clk_restore_context, peripheral
>>>>>>>> clk_hw RST & CLK enables happen thru its corresponding save/restore
>>>>>>>> after source restore
>>>>>>> Also, to align both CLK & RST to the exact state of register, doing
>>>>>>> save/restore in tegra_clk_periph_gate_ops and invoking this after
>>>>>>> source restore for peripheral clock, seems cleaner to avoid any
>>>>>>> misconfiguration b/w rst & clk settings.
>>>>>>>
>>>>> It looks to me that it is very wasteful to store/restore each
>>>>> individual
>>>>> gate and reset state, also given that some of them are shared. I think
>>>>> that the gates and resets should be restored separately for the
>>>>> peripherals by a custom tegra_clk_save/restore_periph_gates/resets().
>>>> clk_periph_fixed_disable just disables clock only without deasserting
>>>> the corresponding peripheral.
>>>>
>>>> corresponding peripheral drivers can also issue reset assert/deassert
>>>> thru reset_control_assert/deassert.
>>>>
>>>> So, we will not get the actual state of clk and rst unless we read and
>>>> save state of reset and clock separately during save_context.
>>>>
>>>> Currently patch is already using custom
>>>> tegra_clk_periph_fixed_save/restore_context for corresponding clk_ops.
>>>> Are you suggesting to do save and restore of complete CLK_ENB/RST_DEV
>>>> register settings instead of individual peripheral bits?
>>> Yes, I'm suggesting to do a complete ungate/reset handling of the
>>> devices in a separate function. All enabling/deassertion will be done in
>>> a single hop, hence using 7us delay and four u32 words, which is much
>>> nicer IMHO.
>> Actually six words, three for CLKs and three for RSTs.
> 
> OK, So with separate function doing complete register save/restore for
> clk & rst, we can't do this thru clk_ops save/restore as clk_ops
> save_restore happens per peripheral wise. So if we decide to do this,
> then this should be invoked in clk-tegra210 driver suspend/resume.

Yes, per-clock save/restore should be used for setting rate and parent.
The ungating and resetting could be done separately to keep things cleaner.

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-08-02 21:15                                     ` Dmitry Osipenko
@ 2019-08-02 21:18                                       ` Sowjanya Komatineni
  2019-08-02 23:51                                       ` Sowjanya Komatineni
  1 sibling, 0 replies; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-08-02 21:18 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree


On 8/2/19 2:15 PM, Dmitry Osipenko wrote:
> 02.08.2019 23:32, Sowjanya Komatineni пишет:
>> On 8/2/19 1:17 PM, Dmitry Osipenko wrote:
>>> 02.08.2019 23:13, Dmitry Osipenko пишет:
>>>> 02.08.2019 21:33, Sowjanya Komatineni пишет:
>>>>> On 8/2/19 5:38 AM, Dmitry Osipenko wrote:
>>>>>> 02.08.2019 2:49, Sowjanya Komatineni пишет:
>>>>>>> On 8/1/19 4:19 PM, Sowjanya Komatineni wrote:
>>>>>>>> On 8/1/19 2:30 PM, Sowjanya Komatineni wrote:
>>>>>>>>> On 8/1/19 1:54 PM, Dmitry Osipenko wrote:
>>>>>>>>>> 01.08.2019 23:31, Sowjanya Komatineni пишет:
>>>>>>>>>>> On 8/1/19 1:17 PM, Dmitry Osipenko wrote:
>>>>>>>>>>>> 01.08.2019 22:42, Sowjanya Komatineni пишет:
>>>>>>>>>>>>> On 8/1/19 12:00 PM, Dmitry Osipenko wrote:
>>>>>>>>>>>>>> 01.08.2019 20:58, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>> On 7/31/19 4:09 PM, Sowjanya Komatineni wrote:
>>>>>>>>>>>>>>>> On 7/31/19 3:44 AM, Dmitry Osipenko wrote:
>>>>>>>>>>>>>>>>> 31.07.2019 12:50, Dmitry Osipenko пишет:
>>>>>>>>>>>>>>>>>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>>>>>> This patch implements save and restore context for
>>>>>>>>>>>>>>>>>>> peripheral
>>>>>>>>>>>>>>>>>>> fixed
>>>>>>>>>>>>>>>>>>> clock ops, peripheral gate clock ops, sdmmc mux clock
>>>>>>>>>>>>>>>>>>> ops, and
>>>>>>>>>>>>>>>>>>> peripheral clock ops.
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> During system suspend, core power goes off and looses the
>>>>>>>>>>>>>>>>>>> settings
>>>>>>>>>>>>>>>>>>> of the Tegra CAR controller registers.
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> So during suspend entry clock and reset state of
>>>>>>>>>>>>>>>>>>> peripherals is
>>>>>>>>>>>>>>>>>>> saved
>>>>>>>>>>>>>>>>>>> and on resume they are restored to have clocks back to
>>>>>>>>>>>>>>>>>>> same
>>>>>>>>>>>>>>>>>>> rate and
>>>>>>>>>>>>>>>>>>> state as before suspend.
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>>>>>>>>>>>>>>>>>> Signed-off-by: Sowjanya Komatineni
>>>>>>>>>>>>>>>>>>> <skomatineni@nvidia.com>
>>>>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>>>>         drivers/clk/tegra/clk-periph-fixed.c | 33
>>>>>>>>>>>>>>>>>>> ++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>         drivers/clk/tegra/clk-periph-gate.c  | 34
>>>>>>>>>>>>>>>>>>> +++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>         drivers/clk/tegra/clk-periph.c       | 37
>>>>>>>>>>>>>>>>>>> ++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>         drivers/clk/tegra/clk-sdmmc-mux.c    | 28
>>>>>>>>>>>>>>>>>>> +++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>         drivers/clk/tegra/clk.h              | 6 ++++++
>>>>>>>>>>>>>>>>>>>         5 files changed, 138 insertions(+)
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>> index c088e7a280df..21b24530fa00 100644
>>>>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>> @@ -60,11 +60,44 @@
>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_recalc_rate(struct
>>>>>>>>>>>>>>>>>>> clk_hw *hw,
>>>>>>>>>>>>>>>>>>>             return (unsigned long)rate;
>>>>>>>>>>>>>>>>>>>         }
>>>>>>>>>>>>>>>>>>>         +static int
>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_save_context(struct
>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    fixed->enb_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>>>>>>>>> fixed->regs->enb_reg) &
>>>>>>>>>>>>>>>>>>> +             mask;
>>>>>>>>>>>>>>>>>>> +    fixed->rst_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>>>>>>>>> fixed->regs->rst_reg) &
>>>>>>>>>>>>>>>>>>> +             mask;
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +static void
>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_restore_context(struct
>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    if (fixed->enb_ctx)
>>>>>>>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>>>>> fixed->regs->enb_set_reg);
>>>>>>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>>>>> fixed->regs->enb_clr_reg);
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    udelay(2);
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    if (!fixed->rst_ctx) {
>>>>>>>>>>>>>>>>>>> +        udelay(5); /* reset propogation delay */
>>>>>>>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>>>>> fixed->regs->rst_reg);
>>>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>         static const struct clk_ops
>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_ops
>>>>>>>>>>>>>>>>>>> = {
>>>>>>>>>>>>>>>>>>>             .is_enabled =
>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_is_enabled,
>>>>>>>>>>>>>>>>>>>             .enable = tegra_clk_periph_fixed_enable,
>>>>>>>>>>>>>>>>>>>             .disable = tegra_clk_periph_fixed_disable,
>>>>>>>>>>>>>>>>>>>             .recalc_rate =
>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_recalc_rate,
>>>>>>>>>>>>>>>>>>> +    .save_context = tegra_clk_periph_fixed_save_context,
>>>>>>>>>>>>>>>>>>> +    .restore_context =
>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_restore_context,
>>>>>>>>>>>>>>>>>>>         };
>>>>>>>>>>>>>>>>>>>           struct clk
>>>>>>>>>>>>>>>>>>> *tegra_clk_register_periph_fixed(const
>>>>>>>>>>>>>>>>>>> char
>>>>>>>>>>>>>>>>>>> *name,
>>>>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>> index 4b31beefc9fc..6ba5b08e0787 100644
>>>>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>> @@ -25,6 +25,8 @@ static
>>>>>>>>>>>>>>>>>>> DEFINE_SPINLOCK(periph_ref_lock);
>>>>>>>>>>>>>>>>>>>           #define read_rst(gate) \
>>>>>>>>>>>>>>>>>>>             readl_relaxed(gate->clk_base +
>>>>>>>>>>>>>>>>>>> (gate->regs->rst_reg))
>>>>>>>>>>>>>>>>>>> +#define write_rst_set(val, gate) \
>>>>>>>>>>>>>>>>>>> +    writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>>>>>>>>> (gate->regs->rst_set_reg))
>>>>>>>>>>>>>>>>>>>         #define write_rst_clr(val, gate) \
>>>>>>>>>>>>>>>>>>>             writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>>>>>>>>> (gate->regs->rst_clr_reg))
>>>>>>>>>>>>>>>>>>>         @@ -110,10 +112,42 @@ static void
>>>>>>>>>>>>>>>>>>> clk_periph_disable(struct
>>>>>>>>>>>>>>>>>>> clk_hw *hw)
>>>>>>>>>>>>>>>>>>> spin_unlock_irqrestore(&periph_ref_lock, flags);
>>>>>>>>>>>>>>>>>>>         }
>>>>>>>>>>>>>>>>>>>         +static int clk_periph_gate_save_context(struct
>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate =
>>>>>>>>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    gate->clk_state_ctx = read_enb(gate) &
>>>>>>>>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>>>>>>>>> +    gate->rst_state_ctx = read_rst(gate) &
>>>>>>>>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +static void clk_periph_gate_restore_context(struct
>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate =
>>>>>>>>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    if (gate->clk_state_ctx)
>>>>>>>>>>>>>>>>>>> + write_enb_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>>>>>>> + write_enb_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    udelay(5);
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
>>>>>>>>>>>>>>>>>>> +        !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
>>>>>>>>>>>>>>>>>>> +        if (gate->rst_state_ctx)
>>>>>>>>>>>>>>>>>>> + write_rst_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>> +        else
>>>>>>>>>>>>>>>>>>> + write_rst_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>         const struct clk_ops tegra_clk_periph_gate_ops = {
>>>>>>>>>>>>>>>>>>>             .is_enabled = clk_periph_is_enabled,
>>>>>>>>>>>>>>>>>>>             .enable = clk_periph_enable,
>>>>>>>>>>>>>>>>>>>             .disable = clk_periph_disable,
>>>>>>>>>>>>>>>>>>> +    .save_context = clk_periph_gate_save_context,
>>>>>>>>>>>>>>>>>>> +    .restore_context = clk_periph_gate_restore_context,
>>>>>>>>>>>>>>>>>>>         };
>>>>>>>>>>>>>>>>>>>           struct clk *tegra_clk_register_periph_gate(const
>>>>>>>>>>>>>>>>>>> char *name,
>>>>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>> index 58437da25156..06fb62955768 100644
>>>>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>> @@ -99,6 +99,37 @@ static void clk_periph_disable(struct
>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>             gate_ops->disable(gate_hw);
>>>>>>>>>>>>>>>>>>>         }
>>>>>>>>>>>>>>>>>>>         +static int clk_periph_save_context(struct
>>>>>>>>>>>>>>>>>>> clk_hw *hw)
>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>>>>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
>>>>>>>>>>>>>>>>>>> +        gate_ops->save_context(gate_hw);
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    periph->parent_ctx = clk_periph_get_parent(hw);
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +static void clk_periph_restore_context(struct clk_hw
>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>>>>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>>>>>>>>> +    const struct clk_ops *div_ops = periph->div_ops;
>>>>>>>>>>>>>>>>>>> +    struct clk_hw *div_hw = &periph->divider.hw;
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    clk_periph_set_parent(hw, periph->parent_ctx);
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
>>>>>>>>>>>>>>>>>>> + div_ops->restore_context(div_hw);
>>>>>>>>>>>>>>>>>> Could you please point to where the divider's
>>>>>>>>>>>>>>>>>> save_context()
>>>>>>>>>>>>>>>>>> happens?
>>>>>>>>>>>>>>>>>> Because I can't see it.
>>>>>>>>>>>>>>>>> Ah, I now see that there is no need to save the dividers
>>>>>>>>>>>>>>>>> context
>>>>>>>>>>>>>>>>> because
>>>>>>>>>>>>>>>>> clk itself has enough info that is needed for the context's
>>>>>>>>>>>>>>>>> restoring
>>>>>>>>>>>>>>>>> (like I pointed in the review to v6).
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Looks like you could also implement a new
>>>>>>>>>>>>>>>>> clk_hw_get_parent_index()
>>>>>>>>>>>>>>>>> generic helper to get the index instead of storing it
>>>>>>>>>>>>>>>>> manually.
>>>>>>>>>>>>>>>> clk_periph_get_parent basically invokes existing clk_mux_ops
>>>>>>>>>>>>>>>> get_parent() which is then saved in tegra_clk_periph.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> All existing drivers are using directly get_parent() from
>>>>>>>>>>>>>>>> clk_mux
>>>>>>>>>>>>>>>> which actually gets index from the register read.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> To have this more generic w.r.t save/restore context
>>>>>>>>>>>>>>>> point of
>>>>>>>>>>>>>>>> view,
>>>>>>>>>>>>>>>> probably instead of implementing new get_parent_index
>>>>>>>>>>>>>>>> helper,
>>>>>>>>>>>>>>>> I think
>>>>>>>>>>>>>>>> its better to implement save_context and restore_context to
>>>>>>>>>>>>>>>> clk_mux_ops along with creating parent_index field into
>>>>>>>>>>>>>>>> clk_mux to
>>>>>>>>>>>>>>>> cache index during set_parent.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> So we just need to invoke mux_ops save_context and
>>>>>>>>>>>>>>>> restore_context.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> I hope its ok to add save/restore context to clk_mux_ops
>>>>>>>>>>>>>>> to be
>>>>>>>>>>>>>>> more
>>>>>>>>>>>>>>> generic w.r.t save/restore context rather than
>>>>>>>>>>>>>>> get_parent_index
>>>>>>>>>>>>>>> API.
>>>>>>>>>>>>>>> Please confirm if you agree.
>>>>>>>>>>>>>> Sounds like a good idea. I see that there is a 'restoring'
>>>>>>>>>>>>>> helper for
>>>>>>>>>>>>>> the generic clk_gate, seems something similar could be done
>>>>>>>>>>>>>> for the
>>>>>>>>>>>>>> clk_mux. And looks like anyway you'll need to associate the
>>>>>>>>>>>>>> parent
>>>>>>>>>>>>>> clock
>>>>>>>>>>>>>> with the hw index in order to restore the muxing.
>>>>>>>>>>>>> by 'restoring' helper for generic clk_gate, are you
>>>>>>>>>>>>> referring to
>>>>>>>>>>>>> clk_gate_restore_context API?
>>>>>>>>>>>> Yes.
>>>>>>>>>>>>
>>>>>>>>>>>>> clk_gate_restore_context is API that's any clk drivers can
>>>>>>>>>>>>> use for
>>>>>>>>>>>>> clk_gate operation restore for custom gate clk_ops.
>>>>>>>>>>>>>
>>>>>>>>>>>>> But clk-periph is directly using generic clk_mux ops from
>>>>>>>>>>>>> clk_mux
>>>>>>>>>>>>> so I
>>>>>>>>>>>>> think we should add .restore_context to clk_mux_ops and then
>>>>>>>>>>>>> during
>>>>>>>>>>>>> clk-periph restore need to invoke mux_ops->restore_context.
>>>>>>>>>>>> I'm not sure whether it will be good for every driver that uses
>>>>>>>>>>>> generic
>>>>>>>>>>>> clk_mux ops. Should be more flexible to have a generic helper
>>>>>>>>>>>> function
>>>>>>>>>>>> that any driver could use in order to restore the clock's
>>>>>>>>>>>> parent.
>>>>>>>>>>>>
>>>>>>>>>>>> The clk-periph restoring also includes case of combining divider
>>>>>>>>>>>> and
>>>>>>>>>>>> parent restoring, so generic helper could be useful in that case
>>>>>>>>>>>> as well.
>>>>>>>>>>>>
>>>>>>>>>>>> It also looks like you could actually use the
>>>>>>>>>>>> clk_gate_restore_context()
>>>>>>>>>>>> instead of manually saving the clock's enable-state, couldn't
>>>>>>>>>>>> you?
>>>>>>>>>>> ok for clk_mux, can add generic clk_mux_restore_context API
>>>>>>>>>>> rather
>>>>>>>>>>> than
>>>>>>>>>>> using restore_context in clk_ops and will invoke that during
>>>>>>>>>>> clk_periph
>>>>>>>>>>> restore.
>>>>>>>>>>>
>>>>>>> digging thru looks like for clk_periph source restore instead of
>>>>>>> clk_mux_restore_context, i can directly do clk_hw_get_parent and
>>>>>>> clk_set_parent with mux_hw as they invoke mux_ops get/set parent
>>>>>>> anyway.
>>>>>>> Will do this for periph clk mux
>>>>>>>>>>> Reg clk_gate, looks like we cant use generic
>>>>>>>>>>> clk_gate_restore_context
>>>>>>>>>>> for clk-periph as it calls enable/disable callbacks and
>>>>>>>>>>> clk_periph_enable/disable in clk-periph-gate also updated
>>>>>>>>>>> refcnt and
>>>>>>>>>>> depending on that actual enable/disable is set.
>>>>>>>>>>>
>>>>>>>>>>> During suspend, peripherals that are already enabled have their
>>>>>>>>>>> refcnt >
>>>>>>>>>>> 1, so they dont go thru enable/disable on restore if we use same
>>>>>>>>>>> enable/disable callback.
>>>>>>>>>> Looks like you could just decrement the gate's enable_refcnt on
>>>>>>>>>> save_context, wouldn't that work?
>>>>>>>>>>
>>>>>>>> gate->enable_refcnt is within clk-periph-gate which gets updated
>>>>>>>> when
>>>>>>>> enable/disable callbacks get execute thru clk_core_enable/disable.
>>>>>>>> But actual enable_count used in clk_gate_restore_context is the one
>>>>>>>> which gets updated with in the clk core enable/disable functions
>>>>>>>> which
>>>>>>>> invokes these callbacks. Depending on this enable_count in clk
>>>>>>>> core it
>>>>>>>> invokes enable/disable.
>>>>>>>>
>>>>>>>> So, this will cause mismatch if we handle refcnt during save/restore
>>>>>>>> of tegra_clk_periph_gate_ops and also enable/disable thru this
>>>>>>>> clk_gate_restore_context is based on enable_count from clk core.
>>>>>>>>
>>>>>>>>>>> Also to align exact reset state along with CLK (like for case
>>>>>>>>>>> where
>>>>>>>>>>> CLK
>>>>>>>>>>> is enabled but peripheral might be in reset state), implemented
>>>>>>>>>>> save/restore in tegra specific tegra_clk_periph_gate_ops
>>>>>>>>>> I'm wondering whether instead of saving/restoring reset-state of
>>>>>>>>>> every
>>>>>>>>>> clock, you could simply save/restore the whole RST_DEV_x_SET
>>>>>>>>>> register.
>>>>>>>>>> Couldn't you?
>>>>>>>>> Thats what I was doing in first version of patch. But later as we
>>>>>>>>> moved to use clk_save_context and clk_restore_context, peripheral
>>>>>>>>> clk_hw RST & CLK enables happen thru its corresponding save/restore
>>>>>>>>> after source restore
>>>>>>>> Also, to align both CLK & RST to the exact state of register, doing
>>>>>>>> save/restore in tegra_clk_periph_gate_ops and invoking this after
>>>>>>>> source restore for peripheral clock, seems cleaner to avoid any
>>>>>>>> misconfiguration b/w rst & clk settings.
>>>>>>>>
>>>>>> It looks to me that it is very wasteful to store/restore each
>>>>>> individual
>>>>>> gate and reset state, also given that some of them are shared. I think
>>>>>> that the gates and resets should be restored separately for the
>>>>>> peripherals by a custom tegra_clk_save/restore_periph_gates/resets().
>>>>> clk_periph_fixed_disable just disables clock only without deasserting
>>>>> the corresponding peripheral.
>>>>>
>>>>> corresponding peripheral drivers can also issue reset assert/deassert
>>>>> thru reset_control_assert/deassert.
>>>>>
>>>>> So, we will not get the actual state of clk and rst unless we read and
>>>>> save state of reset and clock separately during save_context.
>>>>>
>>>>> Currently patch is already using custom
>>>>> tegra_clk_periph_fixed_save/restore_context for corresponding clk_ops.
>>>>> Are you suggesting to do save and restore of complete CLK_ENB/RST_DEV
>>>>> register settings instead of individual peripheral bits?
>>>> Yes, I'm suggesting to do a complete ungate/reset handling of the
>>>> devices in a separate function. All enabling/deassertion will be done in
>>>> a single hop, hence using 7us delay and four u32 words, which is much
>>>> nicer IMHO.
>>> Actually six words, three for CLKs and three for RSTs.
>> OK, So with separate function doing complete register save/restore for
>> clk & rst, we can't do this thru clk_ops save/restore as clk_ops
>> save_restore happens per peripheral wise. So if we decide to do this,
>> then this should be invoked in clk-tegra210 driver suspend/resume.
> Yes, per-clock save/restore should be used for setting rate and parent.
> The ungating and resetting could be done separately to keep things cleaner.
>
OK, Will move back to register wise save/restore for clk_enb/rst_dev in 
next version.

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-08-02 21:15                                     ` Dmitry Osipenko
  2019-08-02 21:18                                       ` Sowjanya Komatineni
@ 2019-08-02 23:51                                       ` Sowjanya Komatineni
  2019-08-03 10:33                                         ` Dmitry Osipenko
  1 sibling, 1 reply; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-08-02 23:51 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree


On 8/2/19 2:15 PM, Dmitry Osipenko wrote:
> 02.08.2019 23:32, Sowjanya Komatineni пишет:
>> On 8/2/19 1:17 PM, Dmitry Osipenko wrote:
>>> 02.08.2019 23:13, Dmitry Osipenko пишет:
>>>> 02.08.2019 21:33, Sowjanya Komatineni пишет:
>>>>> On 8/2/19 5:38 AM, Dmitry Osipenko wrote:
>>>>>> 02.08.2019 2:49, Sowjanya Komatineni пишет:
>>>>>>> On 8/1/19 4:19 PM, Sowjanya Komatineni wrote:
>>>>>>>> On 8/1/19 2:30 PM, Sowjanya Komatineni wrote:
>>>>>>>>> On 8/1/19 1:54 PM, Dmitry Osipenko wrote:
>>>>>>>>>> 01.08.2019 23:31, Sowjanya Komatineni пишет:
>>>>>>>>>>> On 8/1/19 1:17 PM, Dmitry Osipenko wrote:
>>>>>>>>>>>> 01.08.2019 22:42, Sowjanya Komatineni пишет:
>>>>>>>>>>>>> On 8/1/19 12:00 PM, Dmitry Osipenko wrote:
>>>>>>>>>>>>>> 01.08.2019 20:58, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>> On 7/31/19 4:09 PM, Sowjanya Komatineni wrote:
>>>>>>>>>>>>>>>> On 7/31/19 3:44 AM, Dmitry Osipenko wrote:
>>>>>>>>>>>>>>>>> 31.07.2019 12:50, Dmitry Osipenko пишет:
>>>>>>>>>>>>>>>>>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>>>>>> This patch implements save and restore context for
>>>>>>>>>>>>>>>>>>> peripheral
>>>>>>>>>>>>>>>>>>> fixed
>>>>>>>>>>>>>>>>>>> clock ops, peripheral gate clock ops, sdmmc mux clock
>>>>>>>>>>>>>>>>>>> ops, and
>>>>>>>>>>>>>>>>>>> peripheral clock ops.
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> During system suspend, core power goes off and looses the
>>>>>>>>>>>>>>>>>>> settings
>>>>>>>>>>>>>>>>>>> of the Tegra CAR controller registers.
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> So during suspend entry clock and reset state of
>>>>>>>>>>>>>>>>>>> peripherals is
>>>>>>>>>>>>>>>>>>> saved
>>>>>>>>>>>>>>>>>>> and on resume they are restored to have clocks back to
>>>>>>>>>>>>>>>>>>> same
>>>>>>>>>>>>>>>>>>> rate and
>>>>>>>>>>>>>>>>>>> state as before suspend.
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>>>>>>>>>>>>>>>>>> Signed-off-by: Sowjanya Komatineni
>>>>>>>>>>>>>>>>>>> <skomatineni@nvidia.com>
>>>>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>>>>         drivers/clk/tegra/clk-periph-fixed.c | 33
>>>>>>>>>>>>>>>>>>> ++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>         drivers/clk/tegra/clk-periph-gate.c  | 34
>>>>>>>>>>>>>>>>>>> +++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>         drivers/clk/tegra/clk-periph.c       | 37
>>>>>>>>>>>>>>>>>>> ++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>         drivers/clk/tegra/clk-sdmmc-mux.c    | 28
>>>>>>>>>>>>>>>>>>> +++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>         drivers/clk/tegra/clk.h              | 6 ++++++
>>>>>>>>>>>>>>>>>>>         5 files changed, 138 insertions(+)
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>> index c088e7a280df..21b24530fa00 100644
>>>>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>> @@ -60,11 +60,44 @@
>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_recalc_rate(struct
>>>>>>>>>>>>>>>>>>> clk_hw *hw,
>>>>>>>>>>>>>>>>>>>             return (unsigned long)rate;
>>>>>>>>>>>>>>>>>>>         }
>>>>>>>>>>>>>>>>>>>         +static int
>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_save_context(struct
>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    fixed->enb_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>>>>>>>>> fixed->regs->enb_reg) &
>>>>>>>>>>>>>>>>>>> +             mask;
>>>>>>>>>>>>>>>>>>> +    fixed->rst_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>>>>>>>>> fixed->regs->rst_reg) &
>>>>>>>>>>>>>>>>>>> +             mask;
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +static void
>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_restore_context(struct
>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    if (fixed->enb_ctx)
>>>>>>>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>>>>> fixed->regs->enb_set_reg);
>>>>>>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>>>>> fixed->regs->enb_clr_reg);
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    udelay(2);
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    if (!fixed->rst_ctx) {
>>>>>>>>>>>>>>>>>>> +        udelay(5); /* reset propogation delay */
>>>>>>>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>>>>> fixed->regs->rst_reg);
>>>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>         static const struct clk_ops
>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_ops
>>>>>>>>>>>>>>>>>>> = {
>>>>>>>>>>>>>>>>>>>             .is_enabled =
>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_is_enabled,
>>>>>>>>>>>>>>>>>>>             .enable = tegra_clk_periph_fixed_enable,
>>>>>>>>>>>>>>>>>>>             .disable = tegra_clk_periph_fixed_disable,
>>>>>>>>>>>>>>>>>>>             .recalc_rate =
>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_recalc_rate,
>>>>>>>>>>>>>>>>>>> +    .save_context = tegra_clk_periph_fixed_save_context,
>>>>>>>>>>>>>>>>>>> +    .restore_context =
>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_restore_context,
>>>>>>>>>>>>>>>>>>>         };
>>>>>>>>>>>>>>>>>>>           struct clk
>>>>>>>>>>>>>>>>>>> *tegra_clk_register_periph_fixed(const
>>>>>>>>>>>>>>>>>>> char
>>>>>>>>>>>>>>>>>>> *name,
>>>>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>> index 4b31beefc9fc..6ba5b08e0787 100644
>>>>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>> @@ -25,6 +25,8 @@ static
>>>>>>>>>>>>>>>>>>> DEFINE_SPINLOCK(periph_ref_lock);
>>>>>>>>>>>>>>>>>>>           #define read_rst(gate) \
>>>>>>>>>>>>>>>>>>>             readl_relaxed(gate->clk_base +
>>>>>>>>>>>>>>>>>>> (gate->regs->rst_reg))
>>>>>>>>>>>>>>>>>>> +#define write_rst_set(val, gate) \
>>>>>>>>>>>>>>>>>>> +    writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>>>>>>>>> (gate->regs->rst_set_reg))
>>>>>>>>>>>>>>>>>>>         #define write_rst_clr(val, gate) \
>>>>>>>>>>>>>>>>>>>             writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>>>>>>>>> (gate->regs->rst_clr_reg))
>>>>>>>>>>>>>>>>>>>         @@ -110,10 +112,42 @@ static void
>>>>>>>>>>>>>>>>>>> clk_periph_disable(struct
>>>>>>>>>>>>>>>>>>> clk_hw *hw)
>>>>>>>>>>>>>>>>>>> spin_unlock_irqrestore(&periph_ref_lock, flags);
>>>>>>>>>>>>>>>>>>>         }
>>>>>>>>>>>>>>>>>>>         +static int clk_periph_gate_save_context(struct
>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate =
>>>>>>>>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    gate->clk_state_ctx = read_enb(gate) &
>>>>>>>>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>>>>>>>>> +    gate->rst_state_ctx = read_rst(gate) &
>>>>>>>>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +static void clk_periph_gate_restore_context(struct
>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate =
>>>>>>>>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    if (gate->clk_state_ctx)
>>>>>>>>>>>>>>>>>>> + write_enb_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>>>>>>> + write_enb_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    udelay(5);
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
>>>>>>>>>>>>>>>>>>> +        !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
>>>>>>>>>>>>>>>>>>> +        if (gate->rst_state_ctx)
>>>>>>>>>>>>>>>>>>> + write_rst_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>> +        else
>>>>>>>>>>>>>>>>>>> + write_rst_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>         const struct clk_ops tegra_clk_periph_gate_ops = {
>>>>>>>>>>>>>>>>>>>             .is_enabled = clk_periph_is_enabled,
>>>>>>>>>>>>>>>>>>>             .enable = clk_periph_enable,
>>>>>>>>>>>>>>>>>>>             .disable = clk_periph_disable,
>>>>>>>>>>>>>>>>>>> +    .save_context = clk_periph_gate_save_context,
>>>>>>>>>>>>>>>>>>> +    .restore_context = clk_periph_gate_restore_context,
>>>>>>>>>>>>>>>>>>>         };
>>>>>>>>>>>>>>>>>>>           struct clk *tegra_clk_register_periph_gate(const
>>>>>>>>>>>>>>>>>>> char *name,
>>>>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>> index 58437da25156..06fb62955768 100644
>>>>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>> @@ -99,6 +99,37 @@ static void clk_periph_disable(struct
>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>             gate_ops->disable(gate_hw);
>>>>>>>>>>>>>>>>>>>         }
>>>>>>>>>>>>>>>>>>>         +static int clk_periph_save_context(struct
>>>>>>>>>>>>>>>>>>> clk_hw *hw)
>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>>>>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
>>>>>>>>>>>>>>>>>>> +        gate_ops->save_context(gate_hw);
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    periph->parent_ctx = clk_periph_get_parent(hw);
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +static void clk_periph_restore_context(struct clk_hw
>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph *periph = to_clk_periph(hw);
>>>>>>>>>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>>>>>>>>> +    const struct clk_ops *div_ops = periph->div_ops;
>>>>>>>>>>>>>>>>>>> +    struct clk_hw *div_hw = &periph->divider.hw;
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    clk_periph_set_parent(hw, periph->parent_ctx);
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
>>>>>>>>>>>>>>>>>>> + div_ops->restore_context(div_hw);
>>>>>>>>>>>>>>>>>> Could you please point to where the divider's
>>>>>>>>>>>>>>>>>> save_context()
>>>>>>>>>>>>>>>>>> happens?
>>>>>>>>>>>>>>>>>> Because I can't see it.
>>>>>>>>>>>>>>>>> Ah, I now see that there is no need to save the dividers
>>>>>>>>>>>>>>>>> context
>>>>>>>>>>>>>>>>> because
>>>>>>>>>>>>>>>>> clk itself has enough info that is needed for the context's
>>>>>>>>>>>>>>>>> restoring
>>>>>>>>>>>>>>>>> (like I pointed in the review to v6).
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Looks like you could also implement a new
>>>>>>>>>>>>>>>>> clk_hw_get_parent_index()
>>>>>>>>>>>>>>>>> generic helper to get the index instead of storing it
>>>>>>>>>>>>>>>>> manually.
>>>>>>>>>>>>>>>> clk_periph_get_parent basically invokes existing clk_mux_ops
>>>>>>>>>>>>>>>> get_parent() which is then saved in tegra_clk_periph.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> All existing drivers are using directly get_parent() from
>>>>>>>>>>>>>>>> clk_mux
>>>>>>>>>>>>>>>> which actually gets index from the register read.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> To have this more generic w.r.t save/restore context
>>>>>>>>>>>>>>>> point of
>>>>>>>>>>>>>>>> view,
>>>>>>>>>>>>>>>> probably instead of implementing new get_parent_index
>>>>>>>>>>>>>>>> helper,
>>>>>>>>>>>>>>>> I think
>>>>>>>>>>>>>>>> its better to implement save_context and restore_context to
>>>>>>>>>>>>>>>> clk_mux_ops along with creating parent_index field into
>>>>>>>>>>>>>>>> clk_mux to
>>>>>>>>>>>>>>>> cache index during set_parent.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> So we just need to invoke mux_ops save_context and
>>>>>>>>>>>>>>>> restore_context.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> I hope its ok to add save/restore context to clk_mux_ops
>>>>>>>>>>>>>>> to be
>>>>>>>>>>>>>>> more
>>>>>>>>>>>>>>> generic w.r.t save/restore context rather than
>>>>>>>>>>>>>>> get_parent_index
>>>>>>>>>>>>>>> API.
>>>>>>>>>>>>>>> Please confirm if you agree.
>>>>>>>>>>>>>> Sounds like a good idea. I see that there is a 'restoring'
>>>>>>>>>>>>>> helper for
>>>>>>>>>>>>>> the generic clk_gate, seems something similar could be done
>>>>>>>>>>>>>> for the
>>>>>>>>>>>>>> clk_mux. And looks like anyway you'll need to associate the
>>>>>>>>>>>>>> parent
>>>>>>>>>>>>>> clock
>>>>>>>>>>>>>> with the hw index in order to restore the muxing.
>>>>>>>>>>>>> by 'restoring' helper for generic clk_gate, are you
>>>>>>>>>>>>> referring to
>>>>>>>>>>>>> clk_gate_restore_context API?
>>>>>>>>>>>> Yes.
>>>>>>>>>>>>
>>>>>>>>>>>>> clk_gate_restore_context is API that's any clk drivers can
>>>>>>>>>>>>> use for
>>>>>>>>>>>>> clk_gate operation restore for custom gate clk_ops.
>>>>>>>>>>>>>
>>>>>>>>>>>>> But clk-periph is directly using generic clk_mux ops from
>>>>>>>>>>>>> clk_mux
>>>>>>>>>>>>> so I
>>>>>>>>>>>>> think we should add .restore_context to clk_mux_ops and then
>>>>>>>>>>>>> during
>>>>>>>>>>>>> clk-periph restore need to invoke mux_ops->restore_context.
>>>>>>>>>>>> I'm not sure whether it will be good for every driver that uses
>>>>>>>>>>>> generic
>>>>>>>>>>>> clk_mux ops. Should be more flexible to have a generic helper
>>>>>>>>>>>> function
>>>>>>>>>>>> that any driver could use in order to restore the clock's
>>>>>>>>>>>> parent.
>>>>>>>>>>>>
>>>>>>>>>>>> The clk-periph restoring also includes case of combining divider
>>>>>>>>>>>> and
>>>>>>>>>>>> parent restoring, so generic helper could be useful in that case
>>>>>>>>>>>> as well.
>>>>>>>>>>>>
>>>>>>>>>>>> It also looks like you could actually use the
>>>>>>>>>>>> clk_gate_restore_context()
>>>>>>>>>>>> instead of manually saving the clock's enable-state, couldn't
>>>>>>>>>>>> you?
>>>>>>>>>>> ok for clk_mux, can add generic clk_mux_restore_context API
>>>>>>>>>>> rather
>>>>>>>>>>> than
>>>>>>>>>>> using restore_context in clk_ops and will invoke that during
>>>>>>>>>>> clk_periph
>>>>>>>>>>> restore.
>>>>>>>>>>>
>>>>>>> digging thru looks like for clk_periph source restore instead of
>>>>>>> clk_mux_restore_context, i can directly do clk_hw_get_parent and
>>>>>>> clk_set_parent with mux_hw as they invoke mux_ops get/set parent
>>>>>>> anyway.
>>>>>>> Will do this for periph clk mux

Just to be clear, clk_mux don't have cached parent. get_parent is by 
register read. So, cant directly use get_parent and then set during restore.

So will create both clk_mux_save/restore_context in generic clk driver 
and will invoke them during tegra peripheral clock suspend/resume.



>>>>>>>>>>> Reg clk_gate, looks like we cant use generic
>>>>>>>>>>> clk_gate_restore_context
>>>>>>>>>>> for clk-periph as it calls enable/disable callbacks and
>>>>>>>>>>> clk_periph_enable/disable in clk-periph-gate also updated
>>>>>>>>>>> refcnt and
>>>>>>>>>>> depending on that actual enable/disable is set.
>>>>>>>>>>>
>>>>>>>>>>> During suspend, peripherals that are already enabled have their
>>>>>>>>>>> refcnt >
>>>>>>>>>>> 1, so they dont go thru enable/disable on restore if we use same
>>>>>>>>>>> enable/disable callback.
>>>>>>>>>> Looks like you could just decrement the gate's enable_refcnt on
>>>>>>>>>> save_context, wouldn't that work?
>>>>>>>>>>
>>>>>>>> gate->enable_refcnt is within clk-periph-gate which gets updated
>>>>>>>> when
>>>>>>>> enable/disable callbacks get execute thru clk_core_enable/disable.
>>>>>>>> But actual enable_count used in clk_gate_restore_context is the one
>>>>>>>> which gets updated with in the clk core enable/disable functions
>>>>>>>> which
>>>>>>>> invokes these callbacks. Depending on this enable_count in clk
>>>>>>>> core it
>>>>>>>> invokes enable/disable.
>>>>>>>>
>>>>>>>> So, this will cause mismatch if we handle refcnt during save/restore
>>>>>>>> of tegra_clk_periph_gate_ops and also enable/disable thru this
>>>>>>>> clk_gate_restore_context is based on enable_count from clk core.
>>>>>>>>
>>>>>>>>>>> Also to align exact reset state along with CLK (like for case
>>>>>>>>>>> where
>>>>>>>>>>> CLK
>>>>>>>>>>> is enabled but peripheral might be in reset state), implemented
>>>>>>>>>>> save/restore in tegra specific tegra_clk_periph_gate_ops
>>>>>>>>>> I'm wondering whether instead of saving/restoring reset-state of
>>>>>>>>>> every
>>>>>>>>>> clock, you could simply save/restore the whole RST_DEV_x_SET
>>>>>>>>>> register.
>>>>>>>>>> Couldn't you?
>>>>>>>>> Thats what I was doing in first version of patch. But later as we
>>>>>>>>> moved to use clk_save_context and clk_restore_context, peripheral
>>>>>>>>> clk_hw RST & CLK enables happen thru its corresponding save/restore
>>>>>>>>> after source restore
>>>>>>>> Also, to align both CLK & RST to the exact state of register, doing
>>>>>>>> save/restore in tegra_clk_periph_gate_ops and invoking this after
>>>>>>>> source restore for peripheral clock, seems cleaner to avoid any
>>>>>>>> misconfiguration b/w rst & clk settings.
>>>>>>>>
>>>>>> It looks to me that it is very wasteful to store/restore each
>>>>>> individual
>>>>>> gate and reset state, also given that some of them are shared. I think
>>>>>> that the gates and resets should be restored separately for the
>>>>>> peripherals by a custom tegra_clk_save/restore_periph_gates/resets().
>>>>> clk_periph_fixed_disable just disables clock only without deasserting
>>>>> the corresponding peripheral.
>>>>>
>>>>> corresponding peripheral drivers can also issue reset assert/deassert
>>>>> thru reset_control_assert/deassert.
>>>>>
>>>>> So, we will not get the actual state of clk and rst unless we read and
>>>>> save state of reset and clock separately during save_context.
>>>>>
>>>>> Currently patch is already using custom
>>>>> tegra_clk_periph_fixed_save/restore_context for corresponding clk_ops.
>>>>> Are you suggesting to do save and restore of complete CLK_ENB/RST_DEV
>>>>> register settings instead of individual peripheral bits?
>>>> Yes, I'm suggesting to do a complete ungate/reset handling of the
>>>> devices in a separate function. All enabling/deassertion will be done in
>>>> a single hop, hence using 7us delay and four u32 words, which is much
>>>> nicer IMHO.
>>> Actually six words, three for CLKs and three for RSTs.
>> OK, So with separate function doing complete register save/restore for
>> clk & rst, we can't do this thru clk_ops save/restore as clk_ops
>> save_restore happens per peripheral wise. So if we decide to do this,
>> then this should be invoked in clk-tegra210 driver suspend/resume.
> Yes, per-clock save/restore should be used for setting rate and parent.
> The ungating and resetting could be done separately to keep things cleaner.
>

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-08-02 23:51                                       ` Sowjanya Komatineni
@ 2019-08-03 10:33                                         ` Dmitry Osipenko
  2019-08-03 17:01                                           ` Sowjanya Komatineni
  0 siblings, 1 reply; 67+ messages in thread
From: Dmitry Osipenko @ 2019-08-03 10:33 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree

03.08.2019 2:51, Sowjanya Komatineni пишет:
> 
> On 8/2/19 2:15 PM, Dmitry Osipenko wrote:
>> 02.08.2019 23:32, Sowjanya Komatineni пишет:
>>> On 8/2/19 1:17 PM, Dmitry Osipenko wrote:
>>>> 02.08.2019 23:13, Dmitry Osipenko пишет:
>>>>> 02.08.2019 21:33, Sowjanya Komatineni пишет:
>>>>>> On 8/2/19 5:38 AM, Dmitry Osipenko wrote:
>>>>>>> 02.08.2019 2:49, Sowjanya Komatineni пишет:
>>>>>>>> On 8/1/19 4:19 PM, Sowjanya Komatineni wrote:
>>>>>>>>> On 8/1/19 2:30 PM, Sowjanya Komatineni wrote:
>>>>>>>>>> On 8/1/19 1:54 PM, Dmitry Osipenko wrote:
>>>>>>>>>>> 01.08.2019 23:31, Sowjanya Komatineni пишет:
>>>>>>>>>>>> On 8/1/19 1:17 PM, Dmitry Osipenko wrote:
>>>>>>>>>>>>> 01.08.2019 22:42, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>> On 8/1/19 12:00 PM, Dmitry Osipenko wrote:
>>>>>>>>>>>>>>> 01.08.2019 20:58, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>>> On 7/31/19 4:09 PM, Sowjanya Komatineni wrote:
>>>>>>>>>>>>>>>>> On 7/31/19 3:44 AM, Dmitry Osipenko wrote:
>>>>>>>>>>>>>>>>>> 31.07.2019 12:50, Dmitry Osipenko пишет:
>>>>>>>>>>>>>>>>>>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>>>>>>> This patch implements save and restore context for
>>>>>>>>>>>>>>>>>>>> peripheral
>>>>>>>>>>>>>>>>>>>> fixed
>>>>>>>>>>>>>>>>>>>> clock ops, peripheral gate clock ops, sdmmc mux clock
>>>>>>>>>>>>>>>>>>>> ops, and
>>>>>>>>>>>>>>>>>>>> peripheral clock ops.
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> During system suspend, core power goes off and
>>>>>>>>>>>>>>>>>>>> looses the
>>>>>>>>>>>>>>>>>>>> settings
>>>>>>>>>>>>>>>>>>>> of the Tegra CAR controller registers.
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> So during suspend entry clock and reset state of
>>>>>>>>>>>>>>>>>>>> peripherals is
>>>>>>>>>>>>>>>>>>>> saved
>>>>>>>>>>>>>>>>>>>> and on resume they are restored to have clocks back to
>>>>>>>>>>>>>>>>>>>> same
>>>>>>>>>>>>>>>>>>>> rate and
>>>>>>>>>>>>>>>>>>>> state as before suspend.
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>>>>>>>>>>>>>>>>>>> Signed-off-by: Sowjanya Komatineni
>>>>>>>>>>>>>>>>>>>> <skomatineni@nvidia.com>
>>>>>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>>>>>         drivers/clk/tegra/clk-periph-fixed.c | 33
>>>>>>>>>>>>>>>>>>>> ++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>>         drivers/clk/tegra/clk-periph-gate.c  | 34
>>>>>>>>>>>>>>>>>>>> +++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>>         drivers/clk/tegra/clk-periph.c       | 37
>>>>>>>>>>>>>>>>>>>> ++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>>         drivers/clk/tegra/clk-sdmmc-mux.c    | 28
>>>>>>>>>>>>>>>>>>>> +++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>>         drivers/clk/tegra/clk.h              | 6 ++++++
>>>>>>>>>>>>>>>>>>>>         5 files changed, 138 insertions(+)
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>>> index c088e7a280df..21b24530fa00 100644
>>>>>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>>> @@ -60,11 +60,44 @@
>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_recalc_rate(struct
>>>>>>>>>>>>>>>>>>>> clk_hw *hw,
>>>>>>>>>>>>>>>>>>>>             return (unsigned long)rate;
>>>>>>>>>>>>>>>>>>>>         }
>>>>>>>>>>>>>>>>>>>>         +static int
>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_save_context(struct
>>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>> +    fixed->enb_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>>>>>>>>>> fixed->regs->enb_reg) &
>>>>>>>>>>>>>>>>>>>> +             mask;
>>>>>>>>>>>>>>>>>>>> +    fixed->rst_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>>>>>>>>>> fixed->regs->rst_reg) &
>>>>>>>>>>>>>>>>>>>> +             mask;
>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>> +static void
>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_restore_context(struct
>>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>> +    if (fixed->enb_ctx)
>>>>>>>>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>>>>>> fixed->regs->enb_set_reg);
>>>>>>>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>>>>>> fixed->regs->enb_clr_reg);
>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>> +    udelay(2);
>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>> +    if (!fixed->rst_ctx) {
>>>>>>>>>>>>>>>>>>>> +        udelay(5); /* reset propogation delay */
>>>>>>>>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>>>>>> fixed->regs->rst_reg);
>>>>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>         static const struct clk_ops
>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_ops
>>>>>>>>>>>>>>>>>>>> = {
>>>>>>>>>>>>>>>>>>>>             .is_enabled =
>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_is_enabled,
>>>>>>>>>>>>>>>>>>>>             .enable = tegra_clk_periph_fixed_enable,
>>>>>>>>>>>>>>>>>>>>             .disable = tegra_clk_periph_fixed_disable,
>>>>>>>>>>>>>>>>>>>>             .recalc_rate =
>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_recalc_rate,
>>>>>>>>>>>>>>>>>>>> +    .save_context =
>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_save_context,
>>>>>>>>>>>>>>>>>>>> +    .restore_context =
>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_restore_context,
>>>>>>>>>>>>>>>>>>>>         };
>>>>>>>>>>>>>>>>>>>>           struct clk
>>>>>>>>>>>>>>>>>>>> *tegra_clk_register_periph_fixed(const
>>>>>>>>>>>>>>>>>>>> char
>>>>>>>>>>>>>>>>>>>> *name,
>>>>>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>>> index 4b31beefc9fc..6ba5b08e0787 100644
>>>>>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>>> @@ -25,6 +25,8 @@ static
>>>>>>>>>>>>>>>>>>>> DEFINE_SPINLOCK(periph_ref_lock);
>>>>>>>>>>>>>>>>>>>>           #define read_rst(gate) \
>>>>>>>>>>>>>>>>>>>>             readl_relaxed(gate->clk_base +
>>>>>>>>>>>>>>>>>>>> (gate->regs->rst_reg))
>>>>>>>>>>>>>>>>>>>> +#define write_rst_set(val, gate) \
>>>>>>>>>>>>>>>>>>>> +    writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>>>>>>>>>> (gate->regs->rst_set_reg))
>>>>>>>>>>>>>>>>>>>>         #define write_rst_clr(val, gate) \
>>>>>>>>>>>>>>>>>>>>             writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>>>>>>>>>> (gate->regs->rst_clr_reg))
>>>>>>>>>>>>>>>>>>>>         @@ -110,10 +112,42 @@ static void
>>>>>>>>>>>>>>>>>>>> clk_periph_disable(struct
>>>>>>>>>>>>>>>>>>>> clk_hw *hw)
>>>>>>>>>>>>>>>>>>>> spin_unlock_irqrestore(&periph_ref_lock, flags);
>>>>>>>>>>>>>>>>>>>>         }
>>>>>>>>>>>>>>>>>>>>         +static int clk_periph_gate_save_context(struct
>>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate =
>>>>>>>>>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>> +    gate->clk_state_ctx = read_enb(gate) &
>>>>>>>>>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>>>>>>>>>> +    gate->rst_state_ctx = read_rst(gate) &
>>>>>>>>>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>> +static void clk_periph_gate_restore_context(struct
>>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate =
>>>>>>>>>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>> +    if (gate->clk_state_ctx)
>>>>>>>>>>>>>>>>>>>> + write_enb_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>>>>>>>> + write_enb_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>> +    udelay(5);
>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>> +    if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
>>>>>>>>>>>>>>>>>>>> +        !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
>>>>>>>>>>>>>>>>>>>> +        if (gate->rst_state_ctx)
>>>>>>>>>>>>>>>>>>>> + write_rst_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>>> +        else
>>>>>>>>>>>>>>>>>>>> + write_rst_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>         const struct clk_ops
>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_gate_ops = {
>>>>>>>>>>>>>>>>>>>>             .is_enabled = clk_periph_is_enabled,
>>>>>>>>>>>>>>>>>>>>             .enable = clk_periph_enable,
>>>>>>>>>>>>>>>>>>>>             .disable = clk_periph_disable,
>>>>>>>>>>>>>>>>>>>> +    .save_context = clk_periph_gate_save_context,
>>>>>>>>>>>>>>>>>>>> +    .restore_context =
>>>>>>>>>>>>>>>>>>>> clk_periph_gate_restore_context,
>>>>>>>>>>>>>>>>>>>>         };
>>>>>>>>>>>>>>>>>>>>           struct clk
>>>>>>>>>>>>>>>>>>>> *tegra_clk_register_periph_gate(const
>>>>>>>>>>>>>>>>>>>> char *name,
>>>>>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>>> index 58437da25156..06fb62955768 100644
>>>>>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>>> @@ -99,6 +99,37 @@ static void
>>>>>>>>>>>>>>>>>>>> clk_periph_disable(struct
>>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>>             gate_ops->disable(gate_hw);
>>>>>>>>>>>>>>>>>>>>         }
>>>>>>>>>>>>>>>>>>>>         +static int clk_periph_save_context(struct
>>>>>>>>>>>>>>>>>>>> clk_hw *hw)
>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph *periph =
>>>>>>>>>>>>>>>>>>>> to_clk_periph(hw);
>>>>>>>>>>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
>>>>>>>>>>>>>>>>>>>> +        gate_ops->save_context(gate_hw);
>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>> +    periph->parent_ctx = clk_periph_get_parent(hw);
>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>> +static void clk_periph_restore_context(struct clk_hw
>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph *periph =
>>>>>>>>>>>>>>>>>>>> to_clk_periph(hw);
>>>>>>>>>>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>>>>>>>>>> +    const struct clk_ops *div_ops = periph->div_ops;
>>>>>>>>>>>>>>>>>>>> +    struct clk_hw *div_hw = &periph->divider.hw;
>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>> +    clk_periph_set_parent(hw, periph->parent_ctx);
>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
>>>>>>>>>>>>>>>>>>>> + div_ops->restore_context(div_hw);
>>>>>>>>>>>>>>>>>>> Could you please point to where the divider's
>>>>>>>>>>>>>>>>>>> save_context()
>>>>>>>>>>>>>>>>>>> happens?
>>>>>>>>>>>>>>>>>>> Because I can't see it.
>>>>>>>>>>>>>>>>>> Ah, I now see that there is no need to save the dividers
>>>>>>>>>>>>>>>>>> context
>>>>>>>>>>>>>>>>>> because
>>>>>>>>>>>>>>>>>> clk itself has enough info that is needed for the
>>>>>>>>>>>>>>>>>> context's
>>>>>>>>>>>>>>>>>> restoring
>>>>>>>>>>>>>>>>>> (like I pointed in the review to v6).
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> Looks like you could also implement a new
>>>>>>>>>>>>>>>>>> clk_hw_get_parent_index()
>>>>>>>>>>>>>>>>>> generic helper to get the index instead of storing it
>>>>>>>>>>>>>>>>>> manually.
>>>>>>>>>>>>>>>>> clk_periph_get_parent basically invokes existing
>>>>>>>>>>>>>>>>> clk_mux_ops
>>>>>>>>>>>>>>>>> get_parent() which is then saved in tegra_clk_periph.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> All existing drivers are using directly get_parent() from
>>>>>>>>>>>>>>>>> clk_mux
>>>>>>>>>>>>>>>>> which actually gets index from the register read.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> To have this more generic w.r.t save/restore context
>>>>>>>>>>>>>>>>> point of
>>>>>>>>>>>>>>>>> view,
>>>>>>>>>>>>>>>>> probably instead of implementing new get_parent_index
>>>>>>>>>>>>>>>>> helper,
>>>>>>>>>>>>>>>>> I think
>>>>>>>>>>>>>>>>> its better to implement save_context and
>>>>>>>>>>>>>>>>> restore_context to
>>>>>>>>>>>>>>>>> clk_mux_ops along with creating parent_index field into
>>>>>>>>>>>>>>>>> clk_mux to
>>>>>>>>>>>>>>>>> cache index during set_parent.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> So we just need to invoke mux_ops save_context and
>>>>>>>>>>>>>>>>> restore_context.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> I hope its ok to add save/restore context to clk_mux_ops
>>>>>>>>>>>>>>>> to be
>>>>>>>>>>>>>>>> more
>>>>>>>>>>>>>>>> generic w.r.t save/restore context rather than
>>>>>>>>>>>>>>>> get_parent_index
>>>>>>>>>>>>>>>> API.
>>>>>>>>>>>>>>>> Please confirm if you agree.
>>>>>>>>>>>>>>> Sounds like a good idea. I see that there is a 'restoring'
>>>>>>>>>>>>>>> helper for
>>>>>>>>>>>>>>> the generic clk_gate, seems something similar could be done
>>>>>>>>>>>>>>> for the
>>>>>>>>>>>>>>> clk_mux. And looks like anyway you'll need to associate the
>>>>>>>>>>>>>>> parent
>>>>>>>>>>>>>>> clock
>>>>>>>>>>>>>>> with the hw index in order to restore the muxing.
>>>>>>>>>>>>>> by 'restoring' helper for generic clk_gate, are you
>>>>>>>>>>>>>> referring to
>>>>>>>>>>>>>> clk_gate_restore_context API?
>>>>>>>>>>>>> Yes.
>>>>>>>>>>>>>
>>>>>>>>>>>>>> clk_gate_restore_context is API that's any clk drivers can
>>>>>>>>>>>>>> use for
>>>>>>>>>>>>>> clk_gate operation restore for custom gate clk_ops.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> But clk-periph is directly using generic clk_mux ops from
>>>>>>>>>>>>>> clk_mux
>>>>>>>>>>>>>> so I
>>>>>>>>>>>>>> think we should add .restore_context to clk_mux_ops and then
>>>>>>>>>>>>>> during
>>>>>>>>>>>>>> clk-periph restore need to invoke mux_ops->restore_context.
>>>>>>>>>>>>> I'm not sure whether it will be good for every driver that
>>>>>>>>>>>>> uses
>>>>>>>>>>>>> generic
>>>>>>>>>>>>> clk_mux ops. Should be more flexible to have a generic helper
>>>>>>>>>>>>> function
>>>>>>>>>>>>> that any driver could use in order to restore the clock's
>>>>>>>>>>>>> parent.
>>>>>>>>>>>>>
>>>>>>>>>>>>> The clk-periph restoring also includes case of combining
>>>>>>>>>>>>> divider
>>>>>>>>>>>>> and
>>>>>>>>>>>>> parent restoring, so generic helper could be useful in that
>>>>>>>>>>>>> case
>>>>>>>>>>>>> as well.
>>>>>>>>>>>>>
>>>>>>>>>>>>> It also looks like you could actually use the
>>>>>>>>>>>>> clk_gate_restore_context()
>>>>>>>>>>>>> instead of manually saving the clock's enable-state, couldn't
>>>>>>>>>>>>> you?
>>>>>>>>>>>> ok for clk_mux, can add generic clk_mux_restore_context API
>>>>>>>>>>>> rather
>>>>>>>>>>>> than
>>>>>>>>>>>> using restore_context in clk_ops and will invoke that during
>>>>>>>>>>>> clk_periph
>>>>>>>>>>>> restore.
>>>>>>>>>>>>
>>>>>>>> digging thru looks like for clk_periph source restore instead of
>>>>>>>> clk_mux_restore_context, i can directly do clk_hw_get_parent and
>>>>>>>> clk_set_parent with mux_hw as they invoke mux_ops get/set parent
>>>>>>>> anyway.
>>>>>>>> Will do this for periph clk mux
> 
> Just to be clear, clk_mux don't have cached parent. get_parent is by
> register read. So, cant directly use get_parent and then set during
> restore.
> 
> So will create both clk_mux_save/restore_context in generic clk driver
> and will invoke them during tegra peripheral clock suspend/resume.

Why MUX clock doesn't have a cached parent? What MUX clock you're
talking about?

[snip]

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-08-03 10:33                                         ` Dmitry Osipenko
@ 2019-08-03 17:01                                           ` Sowjanya Komatineni
  2019-08-03 23:44                                             ` Sowjanya Komatineni
  0 siblings, 1 reply; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-08-03 17:01 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree


On 8/3/19 3:33 AM, Dmitry Osipenko wrote:
> 03.08.2019 2:51, Sowjanya Komatineni пишет:
>> On 8/2/19 2:15 PM, Dmitry Osipenko wrote:
>>> 02.08.2019 23:32, Sowjanya Komatineni пишет:
>>>> On 8/2/19 1:17 PM, Dmitry Osipenko wrote:
>>>>> 02.08.2019 23:13, Dmitry Osipenko пишет:
>>>>>> 02.08.2019 21:33, Sowjanya Komatineni пишет:
>>>>>>> On 8/2/19 5:38 AM, Dmitry Osipenko wrote:
>>>>>>>> 02.08.2019 2:49, Sowjanya Komatineni пишет:
>>>>>>>>> On 8/1/19 4:19 PM, Sowjanya Komatineni wrote:
>>>>>>>>>> On 8/1/19 2:30 PM, Sowjanya Komatineni wrote:
>>>>>>>>>>> On 8/1/19 1:54 PM, Dmitry Osipenko wrote:
>>>>>>>>>>>> 01.08.2019 23:31, Sowjanya Komatineni пишет:
>>>>>>>>>>>>> On 8/1/19 1:17 PM, Dmitry Osipenko wrote:
>>>>>>>>>>>>>> 01.08.2019 22:42, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>> On 8/1/19 12:00 PM, Dmitry Osipenko wrote:
>>>>>>>>>>>>>>>> 01.08.2019 20:58, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>>>> On 7/31/19 4:09 PM, Sowjanya Komatineni wrote:
>>>>>>>>>>>>>>>>>> On 7/31/19 3:44 AM, Dmitry Osipenko wrote:
>>>>>>>>>>>>>>>>>>> 31.07.2019 12:50, Dmitry Osipenko пишет:
>>>>>>>>>>>>>>>>>>>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>>>>>>>> This patch implements save and restore context for
>>>>>>>>>>>>>>>>>>>>> peripheral
>>>>>>>>>>>>>>>>>>>>> fixed
>>>>>>>>>>>>>>>>>>>>> clock ops, peripheral gate clock ops, sdmmc mux clock
>>>>>>>>>>>>>>>>>>>>> ops, and
>>>>>>>>>>>>>>>>>>>>> peripheral clock ops.
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> During system suspend, core power goes off and
>>>>>>>>>>>>>>>>>>>>> looses the
>>>>>>>>>>>>>>>>>>>>> settings
>>>>>>>>>>>>>>>>>>>>> of the Tegra CAR controller registers.
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> So during suspend entry clock and reset state of
>>>>>>>>>>>>>>>>>>>>> peripherals is
>>>>>>>>>>>>>>>>>>>>> saved
>>>>>>>>>>>>>>>>>>>>> and on resume they are restored to have clocks back to
>>>>>>>>>>>>>>>>>>>>> same
>>>>>>>>>>>>>>>>>>>>> rate and
>>>>>>>>>>>>>>>>>>>>> state as before suspend.
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>>>>>>>>>>>>>>>>>>>> Signed-off-by: Sowjanya Komatineni
>>>>>>>>>>>>>>>>>>>>> <skomatineni@nvidia.com>
>>>>>>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>>>>>>          drivers/clk/tegra/clk-periph-fixed.c | 33
>>>>>>>>>>>>>>>>>>>>> ++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>>>          drivers/clk/tegra/clk-periph-gate.c  | 34
>>>>>>>>>>>>>>>>>>>>> +++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>>>          drivers/clk/tegra/clk-periph.c       | 37
>>>>>>>>>>>>>>>>>>>>> ++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>>>          drivers/clk/tegra/clk-sdmmc-mux.c    | 28
>>>>>>>>>>>>>>>>>>>>> +++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>>>          drivers/clk/tegra/clk.h              | 6 ++++++
>>>>>>>>>>>>>>>>>>>>>          5 files changed, 138 insertions(+)
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>>>> index c088e7a280df..21b24530fa00 100644
>>>>>>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>>>> @@ -60,11 +60,44 @@
>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_recalc_rate(struct
>>>>>>>>>>>>>>>>>>>>> clk_hw *hw,
>>>>>>>>>>>>>>>>>>>>>              return (unsigned long)rate;
>>>>>>>>>>>>>>>>>>>>>          }
>>>>>>>>>>>>>>>>>>>>>          +static int
>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_save_context(struct
>>>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +    fixed->enb_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>>>>>>>>>>> fixed->regs->enb_reg) &
>>>>>>>>>>>>>>>>>>>>> +             mask;
>>>>>>>>>>>>>>>>>>>>> +    fixed->rst_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>>>>>>>>>>> fixed->regs->rst_reg) &
>>>>>>>>>>>>>>>>>>>>> +             mask;
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +static void
>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_restore_context(struct
>>>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +    if (fixed->enb_ctx)
>>>>>>>>>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>>>>>>> fixed->regs->enb_set_reg);
>>>>>>>>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>>>>>>> fixed->regs->enb_clr_reg);
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +    udelay(2);
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +    if (!fixed->rst_ctx) {
>>>>>>>>>>>>>>>>>>>>> +        udelay(5); /* reset propogation delay */
>>>>>>>>>>>>>>>>>>>>> +        writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>>>>>>> fixed->regs->rst_reg);
>>>>>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>          static const struct clk_ops
>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_ops
>>>>>>>>>>>>>>>>>>>>> = {
>>>>>>>>>>>>>>>>>>>>>              .is_enabled =
>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_is_enabled,
>>>>>>>>>>>>>>>>>>>>>              .enable = tegra_clk_periph_fixed_enable,
>>>>>>>>>>>>>>>>>>>>>              .disable = tegra_clk_periph_fixed_disable,
>>>>>>>>>>>>>>>>>>>>>              .recalc_rate =
>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_recalc_rate,
>>>>>>>>>>>>>>>>>>>>> +    .save_context =
>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_save_context,
>>>>>>>>>>>>>>>>>>>>> +    .restore_context =
>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_restore_context,
>>>>>>>>>>>>>>>>>>>>>          };
>>>>>>>>>>>>>>>>>>>>>            struct clk
>>>>>>>>>>>>>>>>>>>>> *tegra_clk_register_periph_fixed(const
>>>>>>>>>>>>>>>>>>>>> char
>>>>>>>>>>>>>>>>>>>>> *name,
>>>>>>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>>>> index 4b31beefc9fc..6ba5b08e0787 100644
>>>>>>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>>>> @@ -25,6 +25,8 @@ static
>>>>>>>>>>>>>>>>>>>>> DEFINE_SPINLOCK(periph_ref_lock);
>>>>>>>>>>>>>>>>>>>>>            #define read_rst(gate) \
>>>>>>>>>>>>>>>>>>>>>              readl_relaxed(gate->clk_base +
>>>>>>>>>>>>>>>>>>>>> (gate->regs->rst_reg))
>>>>>>>>>>>>>>>>>>>>> +#define write_rst_set(val, gate) \
>>>>>>>>>>>>>>>>>>>>> +    writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>>>>>>>>>>> (gate->regs->rst_set_reg))
>>>>>>>>>>>>>>>>>>>>>          #define write_rst_clr(val, gate) \
>>>>>>>>>>>>>>>>>>>>>              writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>>>>>>>>>>> (gate->regs->rst_clr_reg))
>>>>>>>>>>>>>>>>>>>>>          @@ -110,10 +112,42 @@ static void
>>>>>>>>>>>>>>>>>>>>> clk_periph_disable(struct
>>>>>>>>>>>>>>>>>>>>> clk_hw *hw)
>>>>>>>>>>>>>>>>>>>>> spin_unlock_irqrestore(&periph_ref_lock, flags);
>>>>>>>>>>>>>>>>>>>>>          }
>>>>>>>>>>>>>>>>>>>>>          +static int clk_periph_gate_save_context(struct
>>>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate =
>>>>>>>>>>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +    gate->clk_state_ctx = read_enb(gate) &
>>>>>>>>>>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>>>>>>>>>>> +    gate->rst_state_ctx = read_rst(gate) &
>>>>>>>>>>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +static void clk_periph_gate_restore_context(struct
>>>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate =
>>>>>>>>>>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +    if (gate->clk_state_ctx)
>>>>>>>>>>>>>>>>>>>>> + write_enb_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>>>>>>>>> + write_enb_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +    udelay(5);
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +    if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
>>>>>>>>>>>>>>>>>>>>> +        !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
>>>>>>>>>>>>>>>>>>>>> +        if (gate->rst_state_ctx)
>>>>>>>>>>>>>>>>>>>>> + write_rst_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>>>> +        else
>>>>>>>>>>>>>>>>>>>>> + write_rst_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>          const struct clk_ops
>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_gate_ops = {
>>>>>>>>>>>>>>>>>>>>>              .is_enabled = clk_periph_is_enabled,
>>>>>>>>>>>>>>>>>>>>>              .enable = clk_periph_enable,
>>>>>>>>>>>>>>>>>>>>>              .disable = clk_periph_disable,
>>>>>>>>>>>>>>>>>>>>> +    .save_context = clk_periph_gate_save_context,
>>>>>>>>>>>>>>>>>>>>> +    .restore_context =
>>>>>>>>>>>>>>>>>>>>> clk_periph_gate_restore_context,
>>>>>>>>>>>>>>>>>>>>>          };
>>>>>>>>>>>>>>>>>>>>>            struct clk
>>>>>>>>>>>>>>>>>>>>> *tegra_clk_register_periph_gate(const
>>>>>>>>>>>>>>>>>>>>> char *name,
>>>>>>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>>>> index 58437da25156..06fb62955768 100644
>>>>>>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>>>> @@ -99,6 +99,37 @@ static void
>>>>>>>>>>>>>>>>>>>>> clk_periph_disable(struct
>>>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>>>              gate_ops->disable(gate_hw);
>>>>>>>>>>>>>>>>>>>>>          }
>>>>>>>>>>>>>>>>>>>>>          +static int clk_periph_save_context(struct
>>>>>>>>>>>>>>>>>>>>> clk_hw *hw)
>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph *periph =
>>>>>>>>>>>>>>>>>>>>> to_clk_periph(hw);
>>>>>>>>>>>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
>>>>>>>>>>>>>>>>>>>>> +        gate_ops->save_context(gate_hw);
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +    periph->parent_ctx = clk_periph_get_parent(hw);
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +static void clk_periph_restore_context(struct clk_hw
>>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph *periph =
>>>>>>>>>>>>>>>>>>>>> to_clk_periph(hw);
>>>>>>>>>>>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>>>>>>>>>>> +    const struct clk_ops *div_ops = periph->div_ops;
>>>>>>>>>>>>>>>>>>>>> +    struct clk_hw *div_hw = &periph->divider.hw;
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +    clk_periph_set_parent(hw, periph->parent_ctx);
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
>>>>>>>>>>>>>>>>>>>>> + div_ops->restore_context(div_hw);
>>>>>>>>>>>>>>>>>>>> Could you please point to where the divider's
>>>>>>>>>>>>>>>>>>>> save_context()
>>>>>>>>>>>>>>>>>>>> happens?
>>>>>>>>>>>>>>>>>>>> Because I can't see it.
>>>>>>>>>>>>>>>>>>> Ah, I now see that there is no need to save the dividers
>>>>>>>>>>>>>>>>>>> context
>>>>>>>>>>>>>>>>>>> because
>>>>>>>>>>>>>>>>>>> clk itself has enough info that is needed for the
>>>>>>>>>>>>>>>>>>> context's
>>>>>>>>>>>>>>>>>>> restoring
>>>>>>>>>>>>>>>>>>> (like I pointed in the review to v6).
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> Looks like you could also implement a new
>>>>>>>>>>>>>>>>>>> clk_hw_get_parent_index()
>>>>>>>>>>>>>>>>>>> generic helper to get the index instead of storing it
>>>>>>>>>>>>>>>>>>> manually.
>>>>>>>>>>>>>>>>>> clk_periph_get_parent basically invokes existing
>>>>>>>>>>>>>>>>>> clk_mux_ops
>>>>>>>>>>>>>>>>>> get_parent() which is then saved in tegra_clk_periph.
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> All existing drivers are using directly get_parent() from
>>>>>>>>>>>>>>>>>> clk_mux
>>>>>>>>>>>>>>>>>> which actually gets index from the register read.
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> To have this more generic w.r.t save/restore context
>>>>>>>>>>>>>>>>>> point of
>>>>>>>>>>>>>>>>>> view,
>>>>>>>>>>>>>>>>>> probably instead of implementing new get_parent_index
>>>>>>>>>>>>>>>>>> helper,
>>>>>>>>>>>>>>>>>> I think
>>>>>>>>>>>>>>>>>> its better to implement save_context and
>>>>>>>>>>>>>>>>>> restore_context to
>>>>>>>>>>>>>>>>>> clk_mux_ops along with creating parent_index field into
>>>>>>>>>>>>>>>>>> clk_mux to
>>>>>>>>>>>>>>>>>> cache index during set_parent.
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> So we just need to invoke mux_ops save_context and
>>>>>>>>>>>>>>>>>> restore_context.
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> I hope its ok to add save/restore context to clk_mux_ops
>>>>>>>>>>>>>>>>> to be
>>>>>>>>>>>>>>>>> more
>>>>>>>>>>>>>>>>> generic w.r.t save/restore context rather than
>>>>>>>>>>>>>>>>> get_parent_index
>>>>>>>>>>>>>>>>> API.
>>>>>>>>>>>>>>>>> Please confirm if you agree.
>>>>>>>>>>>>>>>> Sounds like a good idea. I see that there is a 'restoring'
>>>>>>>>>>>>>>>> helper for
>>>>>>>>>>>>>>>> the generic clk_gate, seems something similar could be done
>>>>>>>>>>>>>>>> for the
>>>>>>>>>>>>>>>> clk_mux. And looks like anyway you'll need to associate the
>>>>>>>>>>>>>>>> parent
>>>>>>>>>>>>>>>> clock
>>>>>>>>>>>>>>>> with the hw index in order to restore the muxing.
>>>>>>>>>>>>>>> by 'restoring' helper for generic clk_gate, are you
>>>>>>>>>>>>>>> referring to
>>>>>>>>>>>>>>> clk_gate_restore_context API?
>>>>>>>>>>>>>> Yes.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> clk_gate_restore_context is API that's any clk drivers can
>>>>>>>>>>>>>>> use for
>>>>>>>>>>>>>>> clk_gate operation restore for custom gate clk_ops.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> But clk-periph is directly using generic clk_mux ops from
>>>>>>>>>>>>>>> clk_mux
>>>>>>>>>>>>>>> so I
>>>>>>>>>>>>>>> think we should add .restore_context to clk_mux_ops and then
>>>>>>>>>>>>>>> during
>>>>>>>>>>>>>>> clk-periph restore need to invoke mux_ops->restore_context.
>>>>>>>>>>>>>> I'm not sure whether it will be good for every driver that
>>>>>>>>>>>>>> uses
>>>>>>>>>>>>>> generic
>>>>>>>>>>>>>> clk_mux ops. Should be more flexible to have a generic helper
>>>>>>>>>>>>>> function
>>>>>>>>>>>>>> that any driver could use in order to restore the clock's
>>>>>>>>>>>>>> parent.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> The clk-periph restoring also includes case of combining
>>>>>>>>>>>>>> divider
>>>>>>>>>>>>>> and
>>>>>>>>>>>>>> parent restoring, so generic helper could be useful in that
>>>>>>>>>>>>>> case
>>>>>>>>>>>>>> as well.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> It also looks like you could actually use the
>>>>>>>>>>>>>> clk_gate_restore_context()
>>>>>>>>>>>>>> instead of manually saving the clock's enable-state, couldn't
>>>>>>>>>>>>>> you?
>>>>>>>>>>>>> ok for clk_mux, can add generic clk_mux_restore_context API
>>>>>>>>>>>>> rather
>>>>>>>>>>>>> than
>>>>>>>>>>>>> using restore_context in clk_ops and will invoke that during
>>>>>>>>>>>>> clk_periph
>>>>>>>>>>>>> restore.
>>>>>>>>>>>>>
>>>>>>>>> digging thru looks like for clk_periph source restore instead of
>>>>>>>>> clk_mux_restore_context, i can directly do clk_hw_get_parent and
>>>>>>>>> clk_set_parent with mux_hw as they invoke mux_ops get/set parent
>>>>>>>>> anyway.
>>>>>>>>> Will do this for periph clk mux
>> Just to be clear, clk_mux don't have cached parent. get_parent is by
>> register read. So, cant directly use get_parent and then set during
>> restore.
>>
>> So will create both clk_mux_save/restore_context in generic clk driver
>> and will invoke them during tegra peripheral clock suspend/resume.
> Why MUX clock doesn't have a cached parent? What MUX clock you're
> talking about?
>
> [snip]

Please ignore got it.

Will send next version after giving few more days for feedback.

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-08-03 17:01                                           ` Sowjanya Komatineni
@ 2019-08-03 23:44                                             ` Sowjanya Komatineni
  2019-08-04 12:24                                               ` Dmitry Osipenko
  0 siblings, 1 reply; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-08-03 23:44 UTC (permalink / raw)
  To: Dmitry Osipenko, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree


On 8/3/19 10:01 AM, Sowjanya Komatineni wrote:
>
> On 8/3/19 3:33 AM, Dmitry Osipenko wrote:
>> 03.08.2019 2:51, Sowjanya Komatineni пишет:
>>> On 8/2/19 2:15 PM, Dmitry Osipenko wrote:
>>>> 02.08.2019 23:32, Sowjanya Komatineni пишет:
>>>>> On 8/2/19 1:17 PM, Dmitry Osipenko wrote:
>>>>>> 02.08.2019 23:13, Dmitry Osipenko пишет:
>>>>>>> 02.08.2019 21:33, Sowjanya Komatineni пишет:
>>>>>>>> On 8/2/19 5:38 AM, Dmitry Osipenko wrote:
>>>>>>>>> 02.08.2019 2:49, Sowjanya Komatineni пишет:
>>>>>>>>>> On 8/1/19 4:19 PM, Sowjanya Komatineni wrote:
>>>>>>>>>>> On 8/1/19 2:30 PM, Sowjanya Komatineni wrote:
>>>>>>>>>>>> On 8/1/19 1:54 PM, Dmitry Osipenko wrote:
>>>>>>>>>>>>> 01.08.2019 23:31, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>> On 8/1/19 1:17 PM, Dmitry Osipenko wrote:
>>>>>>>>>>>>>>> 01.08.2019 22:42, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>>> On 8/1/19 12:00 PM, Dmitry Osipenko wrote:
>>>>>>>>>>>>>>>>> 01.08.2019 20:58, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>>>>> On 7/31/19 4:09 PM, Sowjanya Komatineni wrote:
>>>>>>>>>>>>>>>>>>> On 7/31/19 3:44 AM, Dmitry Osipenko wrote:
>>>>>>>>>>>>>>>>>>>> 31.07.2019 12:50, Dmitry Osipenko пишет:
>>>>>>>>>>>>>>>>>>>>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>>>>>>>>> This patch implements save and restore context for
>>>>>>>>>>>>>>>>>>>>>> peripheral
>>>>>>>>>>>>>>>>>>>>>> fixed
>>>>>>>>>>>>>>>>>>>>>> clock ops, peripheral gate clock ops, sdmmc mux 
>>>>>>>>>>>>>>>>>>>>>> clock
>>>>>>>>>>>>>>>>>>>>>> ops, and
>>>>>>>>>>>>>>>>>>>>>> peripheral clock ops.
>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>> During system suspend, core power goes off and
>>>>>>>>>>>>>>>>>>>>>> looses the
>>>>>>>>>>>>>>>>>>>>>> settings
>>>>>>>>>>>>>>>>>>>>>> of the Tegra CAR controller registers.
>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>> So during suspend entry clock and reset state of
>>>>>>>>>>>>>>>>>>>>>> peripherals is
>>>>>>>>>>>>>>>>>>>>>> saved
>>>>>>>>>>>>>>>>>>>>>> and on resume they are restored to have clocks 
>>>>>>>>>>>>>>>>>>>>>> back to
>>>>>>>>>>>>>>>>>>>>>> same
>>>>>>>>>>>>>>>>>>>>>> rate and
>>>>>>>>>>>>>>>>>>>>>> state as before suspend.
>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>>>>>>>>>>>>>>>>>>>>> Signed-off-by: Sowjanya Komatineni
>>>>>>>>>>>>>>>>>>>>>> <skomatineni@nvidia.com>
>>>>>>>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>>>>>>> drivers/clk/tegra/clk-periph-fixed.c | 33
>>>>>>>>>>>>>>>>>>>>>> ++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>>>> drivers/clk/tegra/clk-periph-gate.c | 34
>>>>>>>>>>>>>>>>>>>>>> +++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>>>> drivers/clk/tegra/clk-periph.c | 37
>>>>>>>>>>>>>>>>>>>>>> ++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>>>> drivers/clk/tegra/clk-sdmmc-mux.c | 28
>>>>>>>>>>>>>>>>>>>>>> +++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>>>> drivers/clk/tegra/clk.h | 6 ++++++
>>>>>>>>>>>>>>>>>>>>>>          5 files changed, 138 insertions(+)
>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>>>>> index c088e7a280df..21b24530fa00 100644
>>>>>>>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>>>>> @@ -60,11 +60,44 @@
>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_recalc_rate(struct
>>>>>>>>>>>>>>>>>>>>>> clk_hw *hw,
>>>>>>>>>>>>>>>>>>>>>>              return (unsigned long)rate;
>>>>>>>>>>>>>>>>>>>>>>          }
>>>>>>>>>>>>>>>>>>>>>>          +static int
>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_save_context(struct
>>>>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>> +    fixed->enb_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>>>>>>>>>>>> fixed->regs->enb_reg) &
>>>>>>>>>>>>>>>>>>>>>> +             mask;
>>>>>>>>>>>>>>>>>>>>>> +    fixed->rst_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>>>>>>>>>>>> fixed->regs->rst_reg) &
>>>>>>>>>>>>>>>>>>>>>> +             mask;
>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>> +static void
>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_restore_context(struct
>>>>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>> +    if (fixed->enb_ctx)
>>>>>>>>>>>>>>>>>>>>>> + writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>>>>>>>> fixed->regs->enb_set_reg);
>>>>>>>>>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>>>>>>>>>> + writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>>>>>>>> fixed->regs->enb_clr_reg);
>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>> +    udelay(2);
>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>> +    if (!fixed->rst_ctx) {
>>>>>>>>>>>>>>>>>>>>>> +        udelay(5); /* reset propogation delay */
>>>>>>>>>>>>>>>>>>>>>> + writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>>>>>>>> fixed->regs->rst_reg);
>>>>>>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>          static const struct clk_ops
>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_ops
>>>>>>>>>>>>>>>>>>>>>> = {
>>>>>>>>>>>>>>>>>>>>>>              .is_enabled =
>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_is_enabled,
>>>>>>>>>>>>>>>>>>>>>>              .enable = 
>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_enable,
>>>>>>>>>>>>>>>>>>>>>>              .disable = 
>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_disable,
>>>>>>>>>>>>>>>>>>>>>> .recalc_rate =
>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_recalc_rate,
>>>>>>>>>>>>>>>>>>>>>> +    .save_context =
>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_save_context,
>>>>>>>>>>>>>>>>>>>>>> +    .restore_context =
>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_restore_context,
>>>>>>>>>>>>>>>>>>>>>>          };
>>>>>>>>>>>>>>>>>>>>>>            struct clk
>>>>>>>>>>>>>>>>>>>>>> *tegra_clk_register_periph_fixed(const
>>>>>>>>>>>>>>>>>>>>>> char
>>>>>>>>>>>>>>>>>>>>>> *name,
>>>>>>>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>>>>> index 4b31beefc9fc..6ba5b08e0787 100644
>>>>>>>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>>>>> @@ -25,6 +25,8 @@ static
>>>>>>>>>>>>>>>>>>>>>> DEFINE_SPINLOCK(periph_ref_lock);
>>>>>>>>>>>>>>>>>>>>>>            #define read_rst(gate) \
>>>>>>>>>>>>>>>>>>>>>> readl_relaxed(gate->clk_base +
>>>>>>>>>>>>>>>>>>>>>> (gate->regs->rst_reg))
>>>>>>>>>>>>>>>>>>>>>> +#define write_rst_set(val, gate) \
>>>>>>>>>>>>>>>>>>>>>> +    writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>>>>>>>>>>>> (gate->regs->rst_set_reg))
>>>>>>>>>>>>>>>>>>>>>>          #define write_rst_clr(val, gate) \
>>>>>>>>>>>>>>>>>>>>>> writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>>>>>>>>>>>> (gate->regs->rst_clr_reg))
>>>>>>>>>>>>>>>>>>>>>>          @@ -110,10 +112,42 @@ static void
>>>>>>>>>>>>>>>>>>>>>> clk_periph_disable(struct
>>>>>>>>>>>>>>>>>>>>>> clk_hw *hw)
>>>>>>>>>>>>>>>>>>>>>> spin_unlock_irqrestore(&periph_ref_lock, flags);
>>>>>>>>>>>>>>>>>>>>>>          }
>>>>>>>>>>>>>>>>>>>>>>          +static int 
>>>>>>>>>>>>>>>>>>>>>> clk_periph_gate_save_context(struct
>>>>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate =
>>>>>>>>>>>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>> + gate->clk_state_ctx = read_enb(gate) &
>>>>>>>>>>>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>>>>>>>>>>>> + gate->rst_state_ctx = read_rst(gate) &
>>>>>>>>>>>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>> +static void clk_periph_gate_restore_context(struct
>>>>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate =
>>>>>>>>>>>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>> +    if (gate->clk_state_ctx)
>>>>>>>>>>>>>>>>>>>>>> + write_enb_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>>>>>>>>>> + write_enb_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>> +    udelay(5);
>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>> +    if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
>>>>>>>>>>>>>>>>>>>>>> + !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
>>>>>>>>>>>>>>>>>>>>>> +        if (gate->rst_state_ctx)
>>>>>>>>>>>>>>>>>>>>>> + write_rst_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>>>>> +        else
>>>>>>>>>>>>>>>>>>>>>> + write_rst_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>          const struct clk_ops
>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_gate_ops = {
>>>>>>>>>>>>>>>>>>>>>>              .is_enabled = clk_periph_is_enabled,
>>>>>>>>>>>>>>>>>>>>>>              .enable = clk_periph_enable,
>>>>>>>>>>>>>>>>>>>>>>              .disable = clk_periph_disable,
>>>>>>>>>>>>>>>>>>>>>> +    .save_context = clk_periph_gate_save_context,
>>>>>>>>>>>>>>>>>>>>>> +    .restore_context =
>>>>>>>>>>>>>>>>>>>>>> clk_periph_gate_restore_context,
>>>>>>>>>>>>>>>>>>>>>>          };
>>>>>>>>>>>>>>>>>>>>>>            struct clk
>>>>>>>>>>>>>>>>>>>>>> *tegra_clk_register_periph_gate(const
>>>>>>>>>>>>>>>>>>>>>> char *name,
>>>>>>>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>>>>> index 58437da25156..06fb62955768 100644
>>>>>>>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>>>>> @@ -99,6 +99,37 @@ static void
>>>>>>>>>>>>>>>>>>>>>> clk_periph_disable(struct
>>>>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>>>> gate_ops->disable(gate_hw);
>>>>>>>>>>>>>>>>>>>>>>          }
>>>>>>>>>>>>>>>>>>>>>>          +static int clk_periph_save_context(struct
>>>>>>>>>>>>>>>>>>>>>> clk_hw *hw)
>>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph *periph =
>>>>>>>>>>>>>>>>>>>>>> to_clk_periph(hw);
>>>>>>>>>>>>>>>>>>>>>> +    const struct clk_ops *gate_ops = 
>>>>>>>>>>>>>>>>>>>>>> periph->gate_ops;
>>>>>>>>>>>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>> +    if (!(periph->gate.flags & 
>>>>>>>>>>>>>>>>>>>>>> TEGRA_PERIPH_NO_GATE))
>>>>>>>>>>>>>>>>>>>>>> + gate_ops->save_context(gate_hw);
>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>> + periph->parent_ctx = clk_periph_get_parent(hw);
>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>> +static void clk_periph_restore_context(struct 
>>>>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph *periph =
>>>>>>>>>>>>>>>>>>>>>> to_clk_periph(hw);
>>>>>>>>>>>>>>>>>>>>>> +    const struct clk_ops *gate_ops = 
>>>>>>>>>>>>>>>>>>>>>> periph->gate_ops;
>>>>>>>>>>>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>>>>>>>>>>>> +    const struct clk_ops *div_ops = 
>>>>>>>>>>>>>>>>>>>>>> periph->div_ops;
>>>>>>>>>>>>>>>>>>>>>> +    struct clk_hw *div_hw = &periph->divider.hw;
>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>> + clk_periph_set_parent(hw, periph->parent_ctx);
>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>> +    if (!(periph->gate.flags & 
>>>>>>>>>>>>>>>>>>>>>> TEGRA_PERIPH_NO_DIV))
>>>>>>>>>>>>>>>>>>>>>> + div_ops->restore_context(div_hw);
>>>>>>>>>>>>>>>>>>>>> Could you please point to where the divider's
>>>>>>>>>>>>>>>>>>>>> save_context()
>>>>>>>>>>>>>>>>>>>>> happens?
>>>>>>>>>>>>>>>>>>>>> Because I can't see it.
>>>>>>>>>>>>>>>>>>>> Ah, I now see that there is no need to save the 
>>>>>>>>>>>>>>>>>>>> dividers
>>>>>>>>>>>>>>>>>>>> context
>>>>>>>>>>>>>>>>>>>> because
>>>>>>>>>>>>>>>>>>>> clk itself has enough info that is needed for the
>>>>>>>>>>>>>>>>>>>> context's
>>>>>>>>>>>>>>>>>>>> restoring
>>>>>>>>>>>>>>>>>>>> (like I pointed in the review to v6).
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> Looks like you could also implement a new
>>>>>>>>>>>>>>>>>>>> clk_hw_get_parent_index()
>>>>>>>>>>>>>>>>>>>> generic helper to get the index instead of storing it
>>>>>>>>>>>>>>>>>>>> manually.
>>>>>>>>>>>>>>>>>>> clk_periph_get_parent basically invokes existing
>>>>>>>>>>>>>>>>>>> clk_mux_ops
>>>>>>>>>>>>>>>>>>> get_parent() which is then saved in tegra_clk_periph.
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> All existing drivers are using directly get_parent() 
>>>>>>>>>>>>>>>>>>> from
>>>>>>>>>>>>>>>>>>> clk_mux
>>>>>>>>>>>>>>>>>>> which actually gets index from the register read.
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> To have this more generic w.r.t save/restore context
>>>>>>>>>>>>>>>>>>> point of
>>>>>>>>>>>>>>>>>>> view,
>>>>>>>>>>>>>>>>>>> probably instead of implementing new get_parent_index
>>>>>>>>>>>>>>>>>>> helper,
>>>>>>>>>>>>>>>>>>> I think
>>>>>>>>>>>>>>>>>>> its better to implement save_context and
>>>>>>>>>>>>>>>>>>> restore_context to
>>>>>>>>>>>>>>>>>>> clk_mux_ops along with creating parent_index field into
>>>>>>>>>>>>>>>>>>> clk_mux to
>>>>>>>>>>>>>>>>>>> cache index during set_parent.
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> So we just need to invoke mux_ops save_context and
>>>>>>>>>>>>>>>>>>> restore_context.
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> I hope its ok to add save/restore context to clk_mux_ops
>>>>>>>>>>>>>>>>>> to be
>>>>>>>>>>>>>>>>>> more
>>>>>>>>>>>>>>>>>> generic w.r.t save/restore context rather than
>>>>>>>>>>>>>>>>>> get_parent_index
>>>>>>>>>>>>>>>>>> API.
>>>>>>>>>>>>>>>>>> Please confirm if you agree.
>>>>>>>>>>>>>>>>> Sounds like a good idea. I see that there is a 
>>>>>>>>>>>>>>>>> 'restoring'
>>>>>>>>>>>>>>>>> helper for
>>>>>>>>>>>>>>>>> the generic clk_gate, seems something similar could be 
>>>>>>>>>>>>>>>>> done
>>>>>>>>>>>>>>>>> for the
>>>>>>>>>>>>>>>>> clk_mux. And looks like anyway you'll need to 
>>>>>>>>>>>>>>>>> associate the
>>>>>>>>>>>>>>>>> parent
>>>>>>>>>>>>>>>>> clock
>>>>>>>>>>>>>>>>> with the hw index in order to restore the muxing.
>>>>>>>>>>>>>>>> by 'restoring' helper for generic clk_gate, are you
>>>>>>>>>>>>>>>> referring to
>>>>>>>>>>>>>>>> clk_gate_restore_context API?
>>>>>>>>>>>>>>> Yes.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> clk_gate_restore_context is API that's any clk drivers can
>>>>>>>>>>>>>>>> use for
>>>>>>>>>>>>>>>> clk_gate operation restore for custom gate clk_ops.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> But clk-periph is directly using generic clk_mux ops from
>>>>>>>>>>>>>>>> clk_mux
>>>>>>>>>>>>>>>> so I
>>>>>>>>>>>>>>>> think we should add .restore_context to clk_mux_ops and 
>>>>>>>>>>>>>>>> then
>>>>>>>>>>>>>>>> during
>>>>>>>>>>>>>>>> clk-periph restore need to invoke 
>>>>>>>>>>>>>>>> mux_ops->restore_context.
>>>>>>>>>>>>>>> I'm not sure whether it will be good for every driver that
>>>>>>>>>>>>>>> uses
>>>>>>>>>>>>>>> generic
>>>>>>>>>>>>>>> clk_mux ops. Should be more flexible to have a generic 
>>>>>>>>>>>>>>> helper
>>>>>>>>>>>>>>> function
>>>>>>>>>>>>>>> that any driver could use in order to restore the clock's
>>>>>>>>>>>>>>> parent.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> The clk-periph restoring also includes case of combining
>>>>>>>>>>>>>>> divider
>>>>>>>>>>>>>>> and
>>>>>>>>>>>>>>> parent restoring, so generic helper could be useful in that
>>>>>>>>>>>>>>> case
>>>>>>>>>>>>>>> as well.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> It also looks like you could actually use the
>>>>>>>>>>>>>>> clk_gate_restore_context()
>>>>>>>>>>>>>>> instead of manually saving the clock's enable-state, 
>>>>>>>>>>>>>>> couldn't
>>>>>>>>>>>>>>> you?
>>>>>>>>>>>>>> ok for clk_mux, can add generic clk_mux_restore_context API
>>>>>>>>>>>>>> rather
>>>>>>>>>>>>>> than
>>>>>>>>>>>>>> using restore_context in clk_ops and will invoke that during
>>>>>>>>>>>>>> clk_periph
>>>>>>>>>>>>>> restore.
>>>>>>>>>>>>>>
>>>>>>>>>> digging thru looks like for clk_periph source restore instead of
>>>>>>>>>> clk_mux_restore_context, i can directly do clk_hw_get_parent and
>>>>>>>>>> clk_set_parent with mux_hw as they invoke mux_ops get/set parent
>>>>>>>>>> anyway.
>>>>>>>>>> Will do this for periph clk mux
>>> Just to be clear, clk_mux don't have cached parent. get_parent is by
>>> register read. So, cant directly use get_parent and then set during
>>> restore.
>>>
>>> So will create both clk_mux_save/restore_context in generic clk driver
>>> and will invoke them during tegra peripheral clock suspend/resume.
>> Why MUX clock doesn't have a cached parent? What MUX clock you're
>> talking about?
>>
>> [snip]
>
> Please ignore got it.
>
> Will send next version after giving few more days for feedback.
>
Couple of issues:

1.) I see clk-tegra-periph driver periph_clks init_data entries for some 
peripherals are not correct for Tegra 114 and later chips.

Eg I2C TEGRA_INIT_DATA_TABLE entries in clk-tegra-periph are used for 
all Tegra chipsets currently in the driver.

These entries are using MUX shift of 30 and MUX mask only for 2 bits 
which is correct for T30 and prior.
But for later Tegra chips, it should be MUX shift 29 and MASK(3).

Also, I2C parent idx entries in mux_pllp_clkm_idx are different from 
Tegra114 onwards.

As we are using only PLLP and CLKM sources only for I2C, their 
corresponding mux values from register spec by using upper 2 bits for 
T114 onwards match actual 2 bits of MUX value on T30 and prior.

Not sure if this something known pending to port actual clock MUX table 
changes for Tegra114 onwards?

Or

Are we purposely using upper 2 bits only for clock source for T114 and 
later as the upper 2 bit values of the limited clock source we are using 
match with previous Tegra peripheral clock source mux values?

Peter/Thierry, Can you please help comment on this?


2.) Other issue is regarding using clk_set_parent directly during 
clk_peripheral restore is clk_core_set_parent checks new parent with 
current parent and if its same, it just returns as success which is good 
in normal operation.

But during restore, we can't use clk_set_parent as new parent is from 
clk_get_parent on restore and this is same as cached parent.

So clk_set_parent returns 0 but acutal register value for clk source is 
different as it gets reset on SC7 entry/exit and to restore need to 
invoke mux_ops set_parent with parent_index.

So this need parent index for cached parent and without using context 
variable to store this, need an API like you were originally suggesting 
for get_parent_index to get parent index for the specified parent clk_hw.

As we decided not to use save/restore for clk_mux ops as its generic for 
other drivers, looks like we need get_parent_index API to use for 
restoring peripheral source and use this with clk_mux_ops set_parent.

clk core driver already has clk_fetch_parent_index but is it OK to 
export this?

Otherwise, will create separate API in clk driver which returns parent 
index from parent clk_hw by using this existing clk_fetch_parent_index 
so this API can be used by other drivers.

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-08-03 23:44                                             ` Sowjanya Komatineni
@ 2019-08-04 12:24                                               ` Dmitry Osipenko
  2019-08-04 12:31                                                 ` Dmitry Osipenko
  0 siblings, 1 reply; 67+ messages in thread
From: Dmitry Osipenko @ 2019-08-04 12:24 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree

04.08.2019 2:44, Sowjanya Komatineni пишет:
> 
> On 8/3/19 10:01 AM, Sowjanya Komatineni wrote:
>>
>> On 8/3/19 3:33 AM, Dmitry Osipenko wrote:
>>> 03.08.2019 2:51, Sowjanya Komatineni пишет:
>>>> On 8/2/19 2:15 PM, Dmitry Osipenko wrote:
>>>>> 02.08.2019 23:32, Sowjanya Komatineni пишет:
>>>>>> On 8/2/19 1:17 PM, Dmitry Osipenko wrote:
>>>>>>> 02.08.2019 23:13, Dmitry Osipenko пишет:
>>>>>>>> 02.08.2019 21:33, Sowjanya Komatineni пишет:
>>>>>>>>> On 8/2/19 5:38 AM, Dmitry Osipenko wrote:
>>>>>>>>>> 02.08.2019 2:49, Sowjanya Komatineni пишет:
>>>>>>>>>>> On 8/1/19 4:19 PM, Sowjanya Komatineni wrote:
>>>>>>>>>>>> On 8/1/19 2:30 PM, Sowjanya Komatineni wrote:
>>>>>>>>>>>>> On 8/1/19 1:54 PM, Dmitry Osipenko wrote:
>>>>>>>>>>>>>> 01.08.2019 23:31, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>> On 8/1/19 1:17 PM, Dmitry Osipenko wrote:
>>>>>>>>>>>>>>>> 01.08.2019 22:42, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>>>> On 8/1/19 12:00 PM, Dmitry Osipenko wrote:
>>>>>>>>>>>>>>>>>> 01.08.2019 20:58, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>>>>>> On 7/31/19 4:09 PM, Sowjanya Komatineni wrote:
>>>>>>>>>>>>>>>>>>>> On 7/31/19 3:44 AM, Dmitry Osipenko wrote:
>>>>>>>>>>>>>>>>>>>>> 31.07.2019 12:50, Dmitry Osipenko пишет:
>>>>>>>>>>>>>>>>>>>>>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>>>>>>>>>> This patch implements save and restore context for
>>>>>>>>>>>>>>>>>>>>>>> peripheral
>>>>>>>>>>>>>>>>>>>>>>> fixed
>>>>>>>>>>>>>>>>>>>>>>> clock ops, peripheral gate clock ops, sdmmc mux clock
>>>>>>>>>>>>>>>>>>>>>>> ops, and
>>>>>>>>>>>>>>>>>>>>>>> peripheral clock ops.
>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>> During system suspend, core power goes off and
>>>>>>>>>>>>>>>>>>>>>>> looses the
>>>>>>>>>>>>>>>>>>>>>>> settings
>>>>>>>>>>>>>>>>>>>>>>> of the Tegra CAR controller registers.
>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>> So during suspend entry clock and reset state of
>>>>>>>>>>>>>>>>>>>>>>> peripherals is
>>>>>>>>>>>>>>>>>>>>>>> saved
>>>>>>>>>>>>>>>>>>>>>>> and on resume they are restored to have clocks back to
>>>>>>>>>>>>>>>>>>>>>>> same
>>>>>>>>>>>>>>>>>>>>>>> rate and
>>>>>>>>>>>>>>>>>>>>>>> state as before suspend.
>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>>>>>>>>>>>>>>>>>>>>>> Signed-off-by: Sowjanya Komatineni
>>>>>>>>>>>>>>>>>>>>>>> <skomatineni@nvidia.com>
>>>>>>>>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>>>>>>>> drivers/clk/tegra/clk-periph-fixed.c | 33
>>>>>>>>>>>>>>>>>>>>>>> ++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>>>>> drivers/clk/tegra/clk-periph-gate.c | 34
>>>>>>>>>>>>>>>>>>>>>>> +++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>>>>> drivers/clk/tegra/clk-periph.c | 37
>>>>>>>>>>>>>>>>>>>>>>> ++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>>>>> drivers/clk/tegra/clk-sdmmc-mux.c | 28
>>>>>>>>>>>>>>>>>>>>>>> +++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>>>>> drivers/clk/tegra/clk.h | 6 ++++++
>>>>>>>>>>>>>>>>>>>>>>>          5 files changed, 138 insertions(+)
>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>>>>>> index c088e7a280df..21b24530fa00 100644
>>>>>>>>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>>>>>> @@ -60,11 +60,44 @@
>>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_recalc_rate(struct
>>>>>>>>>>>>>>>>>>>>>>> clk_hw *hw,
>>>>>>>>>>>>>>>>>>>>>>>              return (unsigned long)rate;
>>>>>>>>>>>>>>>>>>>>>>>          }
>>>>>>>>>>>>>>>>>>>>>>>          +static int
>>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_save_context(struct
>>>>>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>> +    fixed->enb_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>>>>>>>>>>>>> fixed->regs->enb_reg) &
>>>>>>>>>>>>>>>>>>>>>>> +             mask;
>>>>>>>>>>>>>>>>>>>>>>> +    fixed->rst_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>>>>>>>>>>>>> fixed->regs->rst_reg) &
>>>>>>>>>>>>>>>>>>>>>>> +             mask;
>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>> +static void
>>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_restore_context(struct
>>>>>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>> +    if (fixed->enb_ctx)
>>>>>>>>>>>>>>>>>>>>>>> + writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>>>>>>>>> fixed->regs->enb_set_reg);
>>>>>>>>>>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>>>>>>>>>>> + writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>>>>>>>>> fixed->regs->enb_clr_reg);
>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>> +    udelay(2);
>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>> +    if (!fixed->rst_ctx) {
>>>>>>>>>>>>>>>>>>>>>>> +        udelay(5); /* reset propogation delay */
>>>>>>>>>>>>>>>>>>>>>>> + writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>>>>>>>>> fixed->regs->rst_reg);
>>>>>>>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>>          static const struct clk_ops
>>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_ops
>>>>>>>>>>>>>>>>>>>>>>> = {
>>>>>>>>>>>>>>>>>>>>>>>              .is_enabled =
>>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_is_enabled,
>>>>>>>>>>>>>>>>>>>>>>>              .enable = tegra_clk_periph_fixed_enable,
>>>>>>>>>>>>>>>>>>>>>>>              .disable = tegra_clk_periph_fixed_disable,
>>>>>>>>>>>>>>>>>>>>>>> .recalc_rate =
>>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_recalc_rate,
>>>>>>>>>>>>>>>>>>>>>>> +    .save_context =
>>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_save_context,
>>>>>>>>>>>>>>>>>>>>>>> +    .restore_context =
>>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_restore_context,
>>>>>>>>>>>>>>>>>>>>>>>          };
>>>>>>>>>>>>>>>>>>>>>>>            struct clk
>>>>>>>>>>>>>>>>>>>>>>> *tegra_clk_register_periph_fixed(const
>>>>>>>>>>>>>>>>>>>>>>> char
>>>>>>>>>>>>>>>>>>>>>>> *name,
>>>>>>>>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>>>>>> index 4b31beefc9fc..6ba5b08e0787 100644
>>>>>>>>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>>>>>> @@ -25,6 +25,8 @@ static
>>>>>>>>>>>>>>>>>>>>>>> DEFINE_SPINLOCK(periph_ref_lock);
>>>>>>>>>>>>>>>>>>>>>>>            #define read_rst(gate) \
>>>>>>>>>>>>>>>>>>>>>>> readl_relaxed(gate->clk_base +
>>>>>>>>>>>>>>>>>>>>>>> (gate->regs->rst_reg))
>>>>>>>>>>>>>>>>>>>>>>> +#define write_rst_set(val, gate) \
>>>>>>>>>>>>>>>>>>>>>>> +    writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>>>>>>>>>>>>> (gate->regs->rst_set_reg))
>>>>>>>>>>>>>>>>>>>>>>>          #define write_rst_clr(val, gate) \
>>>>>>>>>>>>>>>>>>>>>>> writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>>>>>>>>>>>>> (gate->regs->rst_clr_reg))
>>>>>>>>>>>>>>>>>>>>>>>          @@ -110,10 +112,42 @@ static void
>>>>>>>>>>>>>>>>>>>>>>> clk_periph_disable(struct
>>>>>>>>>>>>>>>>>>>>>>> clk_hw *hw)
>>>>>>>>>>>>>>>>>>>>>>> spin_unlock_irqrestore(&periph_ref_lock, flags);
>>>>>>>>>>>>>>>>>>>>>>>          }
>>>>>>>>>>>>>>>>>>>>>>>          +static int clk_periph_gate_save_context(struct
>>>>>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate =
>>>>>>>>>>>>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>> + gate->clk_state_ctx = read_enb(gate) &
>>>>>>>>>>>>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>>>>>>>>>>>>> + gate->rst_state_ctx = read_rst(gate) &
>>>>>>>>>>>>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>> +static void clk_periph_gate_restore_context(struct
>>>>>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate =
>>>>>>>>>>>>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>> +    if (gate->clk_state_ctx)
>>>>>>>>>>>>>>>>>>>>>>> + write_enb_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>>>>>>>>>>> + write_enb_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>> +    udelay(5);
>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>> +    if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
>>>>>>>>>>>>>>>>>>>>>>> + !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
>>>>>>>>>>>>>>>>>>>>>>> +        if (gate->rst_state_ctx)
>>>>>>>>>>>>>>>>>>>>>>> + write_rst_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>>>>>> +        else
>>>>>>>>>>>>>>>>>>>>>>> + write_rst_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>>          const struct clk_ops
>>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_gate_ops = {
>>>>>>>>>>>>>>>>>>>>>>>              .is_enabled = clk_periph_is_enabled,
>>>>>>>>>>>>>>>>>>>>>>>              .enable = clk_periph_enable,
>>>>>>>>>>>>>>>>>>>>>>>              .disable = clk_periph_disable,
>>>>>>>>>>>>>>>>>>>>>>> +    .save_context = clk_periph_gate_save_context,
>>>>>>>>>>>>>>>>>>>>>>> +    .restore_context =
>>>>>>>>>>>>>>>>>>>>>>> clk_periph_gate_restore_context,
>>>>>>>>>>>>>>>>>>>>>>>          };
>>>>>>>>>>>>>>>>>>>>>>>            struct clk
>>>>>>>>>>>>>>>>>>>>>>> *tegra_clk_register_periph_gate(const
>>>>>>>>>>>>>>>>>>>>>>> char *name,
>>>>>>>>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>>>>>> index 58437da25156..06fb62955768 100644
>>>>>>>>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>>>>>> @@ -99,6 +99,37 @@ static void
>>>>>>>>>>>>>>>>>>>>>>> clk_periph_disable(struct
>>>>>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>>>>> gate_ops->disable(gate_hw);
>>>>>>>>>>>>>>>>>>>>>>>          }
>>>>>>>>>>>>>>>>>>>>>>>          +static int clk_periph_save_context(struct
>>>>>>>>>>>>>>>>>>>>>>> clk_hw *hw)
>>>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph *periph =
>>>>>>>>>>>>>>>>>>>>>>> to_clk_periph(hw);
>>>>>>>>>>>>>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>>>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
>>>>>>>>>>>>>>>>>>>>>>> + gate_ops->save_context(gate_hw);
>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>> + periph->parent_ctx = clk_periph_get_parent(hw);
>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>> +static void clk_periph_restore_context(struct clk_hw
>>>>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph *periph =
>>>>>>>>>>>>>>>>>>>>>>> to_clk_periph(hw);
>>>>>>>>>>>>>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>>>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>>>>>>>>>>>>> +    const struct clk_ops *div_ops = periph->div_ops;
>>>>>>>>>>>>>>>>>>>>>>> +    struct clk_hw *div_hw = &periph->divider.hw;
>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>> + clk_periph_set_parent(hw, periph->parent_ctx);
>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
>>>>>>>>>>>>>>>>>>>>>>> + div_ops->restore_context(div_hw);
>>>>>>>>>>>>>>>>>>>>>> Could you please point to where the divider's
>>>>>>>>>>>>>>>>>>>>>> save_context()
>>>>>>>>>>>>>>>>>>>>>> happens?
>>>>>>>>>>>>>>>>>>>>>> Because I can't see it.
>>>>>>>>>>>>>>>>>>>>> Ah, I now see that there is no need to save the dividers
>>>>>>>>>>>>>>>>>>>>> context
>>>>>>>>>>>>>>>>>>>>> because
>>>>>>>>>>>>>>>>>>>>> clk itself has enough info that is needed for the
>>>>>>>>>>>>>>>>>>>>> context's
>>>>>>>>>>>>>>>>>>>>> restoring
>>>>>>>>>>>>>>>>>>>>> (like I pointed in the review to v6).
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> Looks like you could also implement a new
>>>>>>>>>>>>>>>>>>>>> clk_hw_get_parent_index()
>>>>>>>>>>>>>>>>>>>>> generic helper to get the index instead of storing it
>>>>>>>>>>>>>>>>>>>>> manually.
>>>>>>>>>>>>>>>>>>>> clk_periph_get_parent basically invokes existing
>>>>>>>>>>>>>>>>>>>> clk_mux_ops
>>>>>>>>>>>>>>>>>>>> get_parent() which is then saved in tegra_clk_periph.
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> All existing drivers are using directly get_parent() from
>>>>>>>>>>>>>>>>>>>> clk_mux
>>>>>>>>>>>>>>>>>>>> which actually gets index from the register read.
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> To have this more generic w.r.t save/restore context
>>>>>>>>>>>>>>>>>>>> point of
>>>>>>>>>>>>>>>>>>>> view,
>>>>>>>>>>>>>>>>>>>> probably instead of implementing new get_parent_index
>>>>>>>>>>>>>>>>>>>> helper,
>>>>>>>>>>>>>>>>>>>> I think
>>>>>>>>>>>>>>>>>>>> its better to implement save_context and
>>>>>>>>>>>>>>>>>>>> restore_context to
>>>>>>>>>>>>>>>>>>>> clk_mux_ops along with creating parent_index field into
>>>>>>>>>>>>>>>>>>>> clk_mux to
>>>>>>>>>>>>>>>>>>>> cache index during set_parent.
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> So we just need to invoke mux_ops save_context and
>>>>>>>>>>>>>>>>>>>> restore_context.
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> I hope its ok to add save/restore context to clk_mux_ops
>>>>>>>>>>>>>>>>>>> to be
>>>>>>>>>>>>>>>>>>> more
>>>>>>>>>>>>>>>>>>> generic w.r.t save/restore context rather than
>>>>>>>>>>>>>>>>>>> get_parent_index
>>>>>>>>>>>>>>>>>>> API.
>>>>>>>>>>>>>>>>>>> Please confirm if you agree.
>>>>>>>>>>>>>>>>>> Sounds like a good idea. I see that there is a 'restoring'
>>>>>>>>>>>>>>>>>> helper for
>>>>>>>>>>>>>>>>>> the generic clk_gate, seems something similar could be done
>>>>>>>>>>>>>>>>>> for the
>>>>>>>>>>>>>>>>>> clk_mux. And looks like anyway you'll need to associate the
>>>>>>>>>>>>>>>>>> parent
>>>>>>>>>>>>>>>>>> clock
>>>>>>>>>>>>>>>>>> with the hw index in order to restore the muxing.
>>>>>>>>>>>>>>>>> by 'restoring' helper for generic clk_gate, are you
>>>>>>>>>>>>>>>>> referring to
>>>>>>>>>>>>>>>>> clk_gate_restore_context API?
>>>>>>>>>>>>>>>> Yes.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> clk_gate_restore_context is API that's any clk drivers can
>>>>>>>>>>>>>>>>> use for
>>>>>>>>>>>>>>>>> clk_gate operation restore for custom gate clk_ops.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> But clk-periph is directly using generic clk_mux ops from
>>>>>>>>>>>>>>>>> clk_mux
>>>>>>>>>>>>>>>>> so I
>>>>>>>>>>>>>>>>> think we should add .restore_context to clk_mux_ops and then
>>>>>>>>>>>>>>>>> during
>>>>>>>>>>>>>>>>> clk-periph restore need to invoke mux_ops->restore_context.
>>>>>>>>>>>>>>>> I'm not sure whether it will be good for every driver that
>>>>>>>>>>>>>>>> uses
>>>>>>>>>>>>>>>> generic
>>>>>>>>>>>>>>>> clk_mux ops. Should be more flexible to have a generic helper
>>>>>>>>>>>>>>>> function
>>>>>>>>>>>>>>>> that any driver could use in order to restore the clock's
>>>>>>>>>>>>>>>> parent.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> The clk-periph restoring also includes case of combining
>>>>>>>>>>>>>>>> divider
>>>>>>>>>>>>>>>> and
>>>>>>>>>>>>>>>> parent restoring, so generic helper could be useful in that
>>>>>>>>>>>>>>>> case
>>>>>>>>>>>>>>>> as well.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> It also looks like you could actually use the
>>>>>>>>>>>>>>>> clk_gate_restore_context()
>>>>>>>>>>>>>>>> instead of manually saving the clock's enable-state, couldn't
>>>>>>>>>>>>>>>> you?
>>>>>>>>>>>>>>> ok for clk_mux, can add generic clk_mux_restore_context API
>>>>>>>>>>>>>>> rather
>>>>>>>>>>>>>>> than
>>>>>>>>>>>>>>> using restore_context in clk_ops and will invoke that during
>>>>>>>>>>>>>>> clk_periph
>>>>>>>>>>>>>>> restore.
>>>>>>>>>>>>>>>
>>>>>>>>>>> digging thru looks like for clk_periph source restore instead of
>>>>>>>>>>> clk_mux_restore_context, i can directly do clk_hw_get_parent and
>>>>>>>>>>> clk_set_parent with mux_hw as they invoke mux_ops get/set parent
>>>>>>>>>>> anyway.
>>>>>>>>>>> Will do this for periph clk mux
>>>> Just to be clear, clk_mux don't have cached parent. get_parent is by
>>>> register read. So, cant directly use get_parent and then set during
>>>> restore.
>>>>
>>>> So will create both clk_mux_save/restore_context in generic clk driver
>>>> and will invoke them during tegra peripheral clock suspend/resume.
>>> Why MUX clock doesn't have a cached parent? What MUX clock you're
>>> talking about?
>>>
>>> [snip]
>>
>> Please ignore got it.
>>
>> Will send next version after giving few more days for feedback.
>>
> Couple of issues:
> 
> 1.) I see clk-tegra-periph driver periph_clks init_data entries for some
> peripherals are not correct for Tegra 114 and later chips.
> 
> Eg I2C TEGRA_INIT_DATA_TABLE entries in clk-tegra-periph are used for all Tegra
> chipsets currently in the driver.
> 
> These entries are using MUX shift of 30 and MUX mask only for 2 bits which is
> correct for T30 and prior.
> But for later Tegra chips, it should be MUX shift 29 and MASK(3).
> 
> Also, I2C parent idx entries in mux_pllp_clkm_idx are different from Tegra114
> onwards.
> 
> As we are using only PLLP and CLKM sources only for I2C, their corresponding mux
> values from register spec by using upper 2 bits for T114 onwards match actual 2
> bits of MUX value on T30 and prior.
> 
> Not sure if this something known pending to port actual clock MUX table changes
> for Tegra114 onwards?
> 
> Or
> 
> Are we purposely using upper 2 bits only for clock source for T114 and later as
> the upper 2 bit values of the limited clock source we are using match with
> previous Tegra peripheral clock source mux values?

The actual hardware values are compatible on all Tegra SoCs and PLLC2 and PLLC3
are just unused on T114+, so should be okay.

> Peter/Thierry, Can you please help comment on this?
> 
> 
> 2.) Other issue is regarding using clk_set_parent directly during clk_peripheral
> restore is clk_core_set_parent checks new parent with current parent and if its
> same, it just returns as success which is good in normal operation.
> 
> But during restore, we can't use clk_set_parent as new parent is from
> clk_get_parent on restore and this is same as cached parent.
> 
> So clk_set_parent returns 0 but acutal register value for clk source is different
> as it gets reset on SC7 entry/exit and to restore need to invoke mux_ops
> set_parent with parent_index.
> 
> So this need parent index for cached parent and without using context variable to
> store this, need an API like you were originally suggesting for get_parent_index
> to get parent index for the specified parent clk_hw.
> 
> As we decided not to use save/restore for clk_mux ops as its generic for other
> drivers, looks like we need get_parent_index API to use for restoring peripheral
> source and use this with clk_mux_ops set_parent.
> 
> clk core driver already has clk_fetch_parent_index but is it OK to export this?
> 
> Otherwise, will create separate API in clk driver which returns parent index from
> parent clk_hw by using this existing clk_fetch_parent_index so this API can be
> used by other drivers.

The clk_fetch_parent_index can't be used directly as you can see because it uses
clk_core which is internal to clk.c, you'll need to make a wrapper for getting the
cached index.

Something like this:

int clk_hw_get_parent_index(struct clk_hw *hw, struct *parent_hw)
{
	if (!hw || !parent_hw)
		return -EINVAL;

	return clk_fetch_parent_index(hw->core, parent_hw->core);
}

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

* Re: [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support
  2019-08-04 12:24                                               ` Dmitry Osipenko
@ 2019-08-04 12:31                                                 ` Dmitry Osipenko
  0 siblings, 0 replies; 67+ messages in thread
From: Dmitry Osipenko @ 2019-08-04 12:31 UTC (permalink / raw)
  To: Sowjanya Komatineni, thierry.reding, jonathanh, tglx, jason,
	marc.zyngier, linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, linux-tegra, linux-kernel, mperttunen, spatra,
	robh+dt, devicetree

04.08.2019 15:24, Dmitry Osipenko пишет:
> 04.08.2019 2:44, Sowjanya Komatineni пишет:
>>
>> On 8/3/19 10:01 AM, Sowjanya Komatineni wrote:
>>>
>>> On 8/3/19 3:33 AM, Dmitry Osipenko wrote:
>>>> 03.08.2019 2:51, Sowjanya Komatineni пишет:
>>>>> On 8/2/19 2:15 PM, Dmitry Osipenko wrote:
>>>>>> 02.08.2019 23:32, Sowjanya Komatineni пишет:
>>>>>>> On 8/2/19 1:17 PM, Dmitry Osipenko wrote:
>>>>>>>> 02.08.2019 23:13, Dmitry Osipenko пишет:
>>>>>>>>> 02.08.2019 21:33, Sowjanya Komatineni пишет:
>>>>>>>>>> On 8/2/19 5:38 AM, Dmitry Osipenko wrote:
>>>>>>>>>>> 02.08.2019 2:49, Sowjanya Komatineni пишет:
>>>>>>>>>>>> On 8/1/19 4:19 PM, Sowjanya Komatineni wrote:
>>>>>>>>>>>>> On 8/1/19 2:30 PM, Sowjanya Komatineni wrote:
>>>>>>>>>>>>>> On 8/1/19 1:54 PM, Dmitry Osipenko wrote:
>>>>>>>>>>>>>>> 01.08.2019 23:31, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>>> On 8/1/19 1:17 PM, Dmitry Osipenko wrote:
>>>>>>>>>>>>>>>>> 01.08.2019 22:42, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>>>>> On 8/1/19 12:00 PM, Dmitry Osipenko wrote:
>>>>>>>>>>>>>>>>>>> 01.08.2019 20:58, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>>>>>>> On 7/31/19 4:09 PM, Sowjanya Komatineni wrote:
>>>>>>>>>>>>>>>>>>>>> On 7/31/19 3:44 AM, Dmitry Osipenko wrote:
>>>>>>>>>>>>>>>>>>>>>> 31.07.2019 12:50, Dmitry Osipenko пишет:
>>>>>>>>>>>>>>>>>>>>>>> 31.07.2019 3:20, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>>>>>>>>>>> This patch implements save and restore context for
>>>>>>>>>>>>>>>>>>>>>>>> peripheral
>>>>>>>>>>>>>>>>>>>>>>>> fixed
>>>>>>>>>>>>>>>>>>>>>>>> clock ops, peripheral gate clock ops, sdmmc mux clock
>>>>>>>>>>>>>>>>>>>>>>>> ops, and
>>>>>>>>>>>>>>>>>>>>>>>> peripheral clock ops.
>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>> During system suspend, core power goes off and
>>>>>>>>>>>>>>>>>>>>>>>> looses the
>>>>>>>>>>>>>>>>>>>>>>>> settings
>>>>>>>>>>>>>>>>>>>>>>>> of the Tegra CAR controller registers.
>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>> So during suspend entry clock and reset state of
>>>>>>>>>>>>>>>>>>>>>>>> peripherals is
>>>>>>>>>>>>>>>>>>>>>>>> saved
>>>>>>>>>>>>>>>>>>>>>>>> and on resume they are restored to have clocks back to
>>>>>>>>>>>>>>>>>>>>>>>> same
>>>>>>>>>>>>>>>>>>>>>>>> rate and
>>>>>>>>>>>>>>>>>>>>>>>> state as before suspend.
>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>> Acked-by: Thierry Reding <treding@nvidia.com>
>>>>>>>>>>>>>>>>>>>>>>>> Signed-off-by: Sowjanya Komatineni
>>>>>>>>>>>>>>>>>>>>>>>> <skomatineni@nvidia.com>
>>>>>>>>>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>>>>>>>>> drivers/clk/tegra/clk-periph-fixed.c | 33
>>>>>>>>>>>>>>>>>>>>>>>> ++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>>>>>> drivers/clk/tegra/clk-periph-gate.c | 34
>>>>>>>>>>>>>>>>>>>>>>>> +++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>>>>>> drivers/clk/tegra/clk-periph.c | 37
>>>>>>>>>>>>>>>>>>>>>>>> ++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>>>>>> drivers/clk/tegra/clk-sdmmc-mux.c | 28
>>>>>>>>>>>>>>>>>>>>>>>> +++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>>>>>> drivers/clk/tegra/clk.h | 6 ++++++
>>>>>>>>>>>>>>>>>>>>>>>>          5 files changed, 138 insertions(+)
>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>>>>>>> index c088e7a280df..21b24530fa00 100644
>>>>>>>>>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-fixed.c
>>>>>>>>>>>>>>>>>>>>>>>> @@ -60,11 +60,44 @@
>>>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_recalc_rate(struct
>>>>>>>>>>>>>>>>>>>>>>>> clk_hw *hw,
>>>>>>>>>>>>>>>>>>>>>>>>              return (unsigned long)rate;
>>>>>>>>>>>>>>>>>>>>>>>>          }
>>>>>>>>>>>>>>>>>>>>>>>>          +static int
>>>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_save_context(struct
>>>>>>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>>>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>>>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>>> +    fixed->enb_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>>>>>>>>>>>>>> fixed->regs->enb_reg) &
>>>>>>>>>>>>>>>>>>>>>>>> +             mask;
>>>>>>>>>>>>>>>>>>>>>>>> +    fixed->rst_ctx = readl_relaxed(fixed->base +
>>>>>>>>>>>>>>>>>>>>>>>> fixed->regs->rst_reg) &
>>>>>>>>>>>>>>>>>>>>>>>> +             mask;
>>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>>> +static void
>>>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_restore_context(struct
>>>>>>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_fixed *fixed =
>>>>>>>>>>>>>>>>>>>>>>>> to_tegra_clk_periph_fixed(hw);
>>>>>>>>>>>>>>>>>>>>>>>> +    u32 mask = 1 << (fixed->num % 32);
>>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>>> +    if (fixed->enb_ctx)
>>>>>>>>>>>>>>>>>>>>>>>> + writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>>>>>>>>>> fixed->regs->enb_set_reg);
>>>>>>>>>>>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>>>>>>>>>>>> + writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>>>>>>>>>> fixed->regs->enb_clr_reg);
>>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>>> +    udelay(2);
>>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>>> +    if (!fixed->rst_ctx) {
>>>>>>>>>>>>>>>>>>>>>>>> +        udelay(5); /* reset propogation delay */
>>>>>>>>>>>>>>>>>>>>>>>> + writel_relaxed(mask, fixed->base +
>>>>>>>>>>>>>>>>>>>>>>>> fixed->regs->rst_reg);
>>>>>>>>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>>>          static const struct clk_ops
>>>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_ops
>>>>>>>>>>>>>>>>>>>>>>>> = {
>>>>>>>>>>>>>>>>>>>>>>>>              .is_enabled =
>>>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_is_enabled,
>>>>>>>>>>>>>>>>>>>>>>>>              .enable = tegra_clk_periph_fixed_enable,
>>>>>>>>>>>>>>>>>>>>>>>>              .disable = tegra_clk_periph_fixed_disable,
>>>>>>>>>>>>>>>>>>>>>>>> .recalc_rate =
>>>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_recalc_rate,
>>>>>>>>>>>>>>>>>>>>>>>> +    .save_context =
>>>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_save_context,
>>>>>>>>>>>>>>>>>>>>>>>> +    .restore_context =
>>>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_fixed_restore_context,
>>>>>>>>>>>>>>>>>>>>>>>>          };
>>>>>>>>>>>>>>>>>>>>>>>>            struct clk
>>>>>>>>>>>>>>>>>>>>>>>> *tegra_clk_register_periph_fixed(const
>>>>>>>>>>>>>>>>>>>>>>>> char
>>>>>>>>>>>>>>>>>>>>>>>> *name,
>>>>>>>>>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>>>>>>> index 4b31beefc9fc..6ba5b08e0787 100644
>>>>>>>>>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph-gate.c
>>>>>>>>>>>>>>>>>>>>>>>> @@ -25,6 +25,8 @@ static
>>>>>>>>>>>>>>>>>>>>>>>> DEFINE_SPINLOCK(periph_ref_lock);
>>>>>>>>>>>>>>>>>>>>>>>>            #define read_rst(gate) \
>>>>>>>>>>>>>>>>>>>>>>>> readl_relaxed(gate->clk_base +
>>>>>>>>>>>>>>>>>>>>>>>> (gate->regs->rst_reg))
>>>>>>>>>>>>>>>>>>>>>>>> +#define write_rst_set(val, gate) \
>>>>>>>>>>>>>>>>>>>>>>>> +    writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>>>>>>>>>>>>>> (gate->regs->rst_set_reg))
>>>>>>>>>>>>>>>>>>>>>>>>          #define write_rst_clr(val, gate) \
>>>>>>>>>>>>>>>>>>>>>>>> writel_relaxed(val, gate->clk_base +
>>>>>>>>>>>>>>>>>>>>>>>> (gate->regs->rst_clr_reg))
>>>>>>>>>>>>>>>>>>>>>>>>          @@ -110,10 +112,42 @@ static void
>>>>>>>>>>>>>>>>>>>>>>>> clk_periph_disable(struct
>>>>>>>>>>>>>>>>>>>>>>>> clk_hw *hw)
>>>>>>>>>>>>>>>>>>>>>>>> spin_unlock_irqrestore(&periph_ref_lock, flags);
>>>>>>>>>>>>>>>>>>>>>>>>          }
>>>>>>>>>>>>>>>>>>>>>>>>          +static int clk_periph_gate_save_context(struct
>>>>>>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate =
>>>>>>>>>>>>>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>>> + gate->clk_state_ctx = read_enb(gate) &
>>>>>>>>>>>>>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>>>>>>>>>>>>>> + gate->rst_state_ctx = read_rst(gate) &
>>>>>>>>>>>>>>>>>>>>>>>> periph_clk_to_bit(gate);
>>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>>> +static void clk_periph_gate_restore_context(struct
>>>>>>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph_gate *gate =
>>>>>>>>>>>>>>>>>>>>>>>> to_clk_periph_gate(hw);
>>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>>> +    if (gate->clk_state_ctx)
>>>>>>>>>>>>>>>>>>>>>>>> + write_enb_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>>>>>>>>>>>> + write_enb_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>>> +    udelay(5);
>>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>>> +    if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
>>>>>>>>>>>>>>>>>>>>>>>> + !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
>>>>>>>>>>>>>>>>>>>>>>>> +        if (gate->rst_state_ctx)
>>>>>>>>>>>>>>>>>>>>>>>> + write_rst_set(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>>>>>>> +        else
>>>>>>>>>>>>>>>>>>>>>>>> + write_rst_clr(periph_clk_to_bit(gate), gate);
>>>>>>>>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>>>          const struct clk_ops
>>>>>>>>>>>>>>>>>>>>>>>> tegra_clk_periph_gate_ops = {
>>>>>>>>>>>>>>>>>>>>>>>>              .is_enabled = clk_periph_is_enabled,
>>>>>>>>>>>>>>>>>>>>>>>>              .enable = clk_periph_enable,
>>>>>>>>>>>>>>>>>>>>>>>>              .disable = clk_periph_disable,
>>>>>>>>>>>>>>>>>>>>>>>> +    .save_context = clk_periph_gate_save_context,
>>>>>>>>>>>>>>>>>>>>>>>> +    .restore_context =
>>>>>>>>>>>>>>>>>>>>>>>> clk_periph_gate_restore_context,
>>>>>>>>>>>>>>>>>>>>>>>>          };
>>>>>>>>>>>>>>>>>>>>>>>>            struct clk
>>>>>>>>>>>>>>>>>>>>>>>> *tegra_clk_register_periph_gate(const
>>>>>>>>>>>>>>>>>>>>>>>> char *name,
>>>>>>>>>>>>>>>>>>>>>>>> diff --git a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>>>>>>> b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>>>>>>> index 58437da25156..06fb62955768 100644
>>>>>>>>>>>>>>>>>>>>>>>> --- a/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>>>>>>> +++ b/drivers/clk/tegra/clk-periph.c
>>>>>>>>>>>>>>>>>>>>>>>> @@ -99,6 +99,37 @@ static void
>>>>>>>>>>>>>>>>>>>>>>>> clk_periph_disable(struct
>>>>>>>>>>>>>>>>>>>>>>>> clk_hw
>>>>>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>>>>>> gate_ops->disable(gate_hw);
>>>>>>>>>>>>>>>>>>>>>>>>          }
>>>>>>>>>>>>>>>>>>>>>>>>          +static int clk_periph_save_context(struct
>>>>>>>>>>>>>>>>>>>>>>>> clk_hw *hw)
>>>>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph *periph =
>>>>>>>>>>>>>>>>>>>>>>>> to_clk_periph(hw);
>>>>>>>>>>>>>>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>>>>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
>>>>>>>>>>>>>>>>>>>>>>>> + gate_ops->save_context(gate_hw);
>>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>>> + periph->parent_ctx = clk_periph_get_parent(hw);
>>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>>> +static void clk_periph_restore_context(struct clk_hw
>>>>>>>>>>>>>>>>>>>>>>>> *hw)
>>>>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>>>>> +    struct tegra_clk_periph *periph =
>>>>>>>>>>>>>>>>>>>>>>>> to_clk_periph(hw);
>>>>>>>>>>>>>>>>>>>>>>>> +    const struct clk_ops *gate_ops = periph->gate_ops;
>>>>>>>>>>>>>>>>>>>>>>>> +    struct clk_hw *gate_hw = &periph->gate.hw;
>>>>>>>>>>>>>>>>>>>>>>>> +    const struct clk_ops *div_ops = periph->div_ops;
>>>>>>>>>>>>>>>>>>>>>>>> +    struct clk_hw *div_hw = &periph->divider.hw;
>>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>>> + clk_periph_set_parent(hw, periph->parent_ctx);
>>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>>> +    if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
>>>>>>>>>>>>>>>>>>>>>>>> + div_ops->restore_context(div_hw);
>>>>>>>>>>>>>>>>>>>>>>> Could you please point to where the divider's
>>>>>>>>>>>>>>>>>>>>>>> save_context()
>>>>>>>>>>>>>>>>>>>>>>> happens?
>>>>>>>>>>>>>>>>>>>>>>> Because I can't see it.
>>>>>>>>>>>>>>>>>>>>>> Ah, I now see that there is no need to save the dividers
>>>>>>>>>>>>>>>>>>>>>> context
>>>>>>>>>>>>>>>>>>>>>> because
>>>>>>>>>>>>>>>>>>>>>> clk itself has enough info that is needed for the
>>>>>>>>>>>>>>>>>>>>>> context's
>>>>>>>>>>>>>>>>>>>>>> restoring
>>>>>>>>>>>>>>>>>>>>>> (like I pointed in the review to v6).
>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>> Looks like you could also implement a new
>>>>>>>>>>>>>>>>>>>>>> clk_hw_get_parent_index()
>>>>>>>>>>>>>>>>>>>>>> generic helper to get the index instead of storing it
>>>>>>>>>>>>>>>>>>>>>> manually.
>>>>>>>>>>>>>>>>>>>>> clk_periph_get_parent basically invokes existing
>>>>>>>>>>>>>>>>>>>>> clk_mux_ops
>>>>>>>>>>>>>>>>>>>>> get_parent() which is then saved in tegra_clk_periph.
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> All existing drivers are using directly get_parent() from
>>>>>>>>>>>>>>>>>>>>> clk_mux
>>>>>>>>>>>>>>>>>>>>> which actually gets index from the register read.
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> To have this more generic w.r.t save/restore context
>>>>>>>>>>>>>>>>>>>>> point of
>>>>>>>>>>>>>>>>>>>>> view,
>>>>>>>>>>>>>>>>>>>>> probably instead of implementing new get_parent_index
>>>>>>>>>>>>>>>>>>>>> helper,
>>>>>>>>>>>>>>>>>>>>> I think
>>>>>>>>>>>>>>>>>>>>> its better to implement save_context and
>>>>>>>>>>>>>>>>>>>>> restore_context to
>>>>>>>>>>>>>>>>>>>>> clk_mux_ops along with creating parent_index field into
>>>>>>>>>>>>>>>>>>>>> clk_mux to
>>>>>>>>>>>>>>>>>>>>> cache index during set_parent.
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> So we just need to invoke mux_ops save_context and
>>>>>>>>>>>>>>>>>>>>> restore_context.
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> I hope its ok to add save/restore context to clk_mux_ops
>>>>>>>>>>>>>>>>>>>> to be
>>>>>>>>>>>>>>>>>>>> more
>>>>>>>>>>>>>>>>>>>> generic w.r.t save/restore context rather than
>>>>>>>>>>>>>>>>>>>> get_parent_index
>>>>>>>>>>>>>>>>>>>> API.
>>>>>>>>>>>>>>>>>>>> Please confirm if you agree.
>>>>>>>>>>>>>>>>>>> Sounds like a good idea. I see that there is a 'restoring'
>>>>>>>>>>>>>>>>>>> helper for
>>>>>>>>>>>>>>>>>>> the generic clk_gate, seems something similar could be done
>>>>>>>>>>>>>>>>>>> for the
>>>>>>>>>>>>>>>>>>> clk_mux. And looks like anyway you'll need to associate the
>>>>>>>>>>>>>>>>>>> parent
>>>>>>>>>>>>>>>>>>> clock
>>>>>>>>>>>>>>>>>>> with the hw index in order to restore the muxing.
>>>>>>>>>>>>>>>>>> by 'restoring' helper for generic clk_gate, are you
>>>>>>>>>>>>>>>>>> referring to
>>>>>>>>>>>>>>>>>> clk_gate_restore_context API?
>>>>>>>>>>>>>>>>> Yes.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> clk_gate_restore_context is API that's any clk drivers can
>>>>>>>>>>>>>>>>>> use for
>>>>>>>>>>>>>>>>>> clk_gate operation restore for custom gate clk_ops.
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> But clk-periph is directly using generic clk_mux ops from
>>>>>>>>>>>>>>>>>> clk_mux
>>>>>>>>>>>>>>>>>> so I
>>>>>>>>>>>>>>>>>> think we should add .restore_context to clk_mux_ops and then
>>>>>>>>>>>>>>>>>> during
>>>>>>>>>>>>>>>>>> clk-periph restore need to invoke mux_ops->restore_context.
>>>>>>>>>>>>>>>>> I'm not sure whether it will be good for every driver that
>>>>>>>>>>>>>>>>> uses
>>>>>>>>>>>>>>>>> generic
>>>>>>>>>>>>>>>>> clk_mux ops. Should be more flexible to have a generic helper
>>>>>>>>>>>>>>>>> function
>>>>>>>>>>>>>>>>> that any driver could use in order to restore the clock's
>>>>>>>>>>>>>>>>> parent.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> The clk-periph restoring also includes case of combining
>>>>>>>>>>>>>>>>> divider
>>>>>>>>>>>>>>>>> and
>>>>>>>>>>>>>>>>> parent restoring, so generic helper could be useful in that
>>>>>>>>>>>>>>>>> case
>>>>>>>>>>>>>>>>> as well.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> It also looks like you could actually use the
>>>>>>>>>>>>>>>>> clk_gate_restore_context()
>>>>>>>>>>>>>>>>> instead of manually saving the clock's enable-state, couldn't
>>>>>>>>>>>>>>>>> you?
>>>>>>>>>>>>>>>> ok for clk_mux, can add generic clk_mux_restore_context API
>>>>>>>>>>>>>>>> rather
>>>>>>>>>>>>>>>> than
>>>>>>>>>>>>>>>> using restore_context in clk_ops and will invoke that during
>>>>>>>>>>>>>>>> clk_periph
>>>>>>>>>>>>>>>> restore.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>> digging thru looks like for clk_periph source restore instead of
>>>>>>>>>>>> clk_mux_restore_context, i can directly do clk_hw_get_parent and
>>>>>>>>>>>> clk_set_parent with mux_hw as they invoke mux_ops get/set parent
>>>>>>>>>>>> anyway.
>>>>>>>>>>>> Will do this for periph clk mux
>>>>> Just to be clear, clk_mux don't have cached parent. get_parent is by
>>>>> register read. So, cant directly use get_parent and then set during
>>>>> restore.
>>>>>
>>>>> So will create both clk_mux_save/restore_context in generic clk driver
>>>>> and will invoke them during tegra peripheral clock suspend/resume.
>>>> Why MUX clock doesn't have a cached parent? What MUX clock you're
>>>> talking about?
>>>>
>>>> [snip]
>>>
>>> Please ignore got it.
>>>
>>> Will send next version after giving few more days for feedback.
>>>
>> Couple of issues:
>>
>> 1.) I see clk-tegra-periph driver periph_clks init_data entries for some
>> peripherals are not correct for Tegra 114 and later chips.
>>
>> Eg I2C TEGRA_INIT_DATA_TABLE entries in clk-tegra-periph are used for all Tegra
>> chipsets currently in the driver.
>>
>> These entries are using MUX shift of 30 and MUX mask only for 2 bits which is
>> correct for T30 and prior.
>> But for later Tegra chips, it should be MUX shift 29 and MASK(3).
>>
>> Also, I2C parent idx entries in mux_pllp_clkm_idx are different from Tegra114
>> onwards.
>>
>> As we are using only PLLP and CLKM sources only for I2C, their corresponding mux
>> values from register spec by using upper 2 bits for T114 onwards match actual 2
>> bits of MUX value on T30 and prior.
>>
>> Not sure if this something known pending to port actual clock MUX table changes
>> for Tegra114 onwards?
>>
>> Or
>>
>> Are we purposely using upper 2 bits only for clock source for T114 and later as
>> the upper 2 bit values of the limited clock source we are using match with
>> previous Tegra peripheral clock source mux values?
> 
> The actual hardware values are compatible on all Tegra SoCs and PLLC2 and PLLC3
> are just unused on T114+, so should be okay.
> 
>> Peter/Thierry, Can you please help comment on this?
>>
>>
>> 2.) Other issue is regarding using clk_set_parent directly during clk_peripheral
>> restore is clk_core_set_parent checks new parent with current parent and if its
>> same, it just returns as success which is good in normal operation.
>>
>> But during restore, we can't use clk_set_parent as new parent is from
>> clk_get_parent on restore and this is same as cached parent.
>>
>> So clk_set_parent returns 0 but acutal register value for clk source is different
>> as it gets reset on SC7 entry/exit and to restore need to invoke mux_ops
>> set_parent with parent_index.
>>
>> So this need parent index for cached parent and without using context variable to
>> store this, need an API like you were originally suggesting for get_parent_index
>> to get parent index for the specified parent clk_hw.
>>
>> As we decided not to use save/restore for clk_mux ops as its generic for other
>> drivers, looks like we need get_parent_index API to use for restoring peripheral
>> source and use this with clk_mux_ops set_parent.
>>
>> clk core driver already has clk_fetch_parent_index but is it OK to export this?
>>
>> Otherwise, will create separate API in clk driver which returns parent index from
>> parent clk_hw by using this existing clk_fetch_parent_index so this API can be
>> used by other drivers.

Actually, you probably meant exactly the same what I wrote below. So
yes, it should be good to extend the CCF API since likely that this case
will be useful for other drivers as well.

> The clk_fetch_parent_index can't be used directly as you can see because it uses
> clk_core which is internal to clk.c, you'll need to make a wrapper for getting the
> cached index.
> 
> Something like this:
> 
> int clk_hw_get_parent_index(struct clk_hw *hw, struct *parent_hw)
> {
> 	if (!hw || !parent_hw)
> 		return -EINVAL;
> 
> 	return clk_fetch_parent_index(hw->core, parent_hw->core);
> }
> 

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

* [PATCH v7 06/20] clk: tegra: Support for OSC context save and restore
  2019-07-31 21:10 [PATCH v7 00/20] SC7 entry and exit support for Tegra210 Sowjanya Komatineni
@ 2019-07-31 21:10 ` Sowjanya Komatineni
  0 siblings, 0 replies; 67+ messages in thread
From: Sowjanya Komatineni @ 2019-07-31 21:10 UTC (permalink / raw)
  To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
	linus.walleij, stefan, mark.rutland
  Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
	josephl, talho, skomatineni, linux-tegra, linux-kernel,
	mperttunen, spatra, robh+dt, digetx, devicetree, rjw,
	viresh.kumar, linux-pm

This patch adds support for saving OSC clock frequency and the
drive-strength during OSC clock init and creates an API to restore
OSC control register value from the saved context.

This API is invoked by Tegra210 clock driver during system resume
to restore the  OSC clock settings.

Acked-by: Thierry Reding <treding@nvidia.com>
Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/clk/tegra/clk-tegra-fixed.c | 15 +++++++++++++++
 drivers/clk/tegra/clk.h             |  1 +
 2 files changed, 16 insertions(+)

diff --git a/drivers/clk/tegra/clk-tegra-fixed.c b/drivers/clk/tegra/clk-tegra-fixed.c
index 8d91b2b191cf..7c6c8abfcde6 100644
--- a/drivers/clk/tegra/clk-tegra-fixed.c
+++ b/drivers/clk/tegra/clk-tegra-fixed.c
@@ -17,6 +17,10 @@
 #define OSC_CTRL			0x50
 #define OSC_CTRL_OSC_FREQ_SHIFT		28
 #define OSC_CTRL_PLL_REF_DIV_SHIFT	26
+#define OSC_CTRL_MASK			(0x3f2 |	\
+					(0xf << OSC_CTRL_OSC_FREQ_SHIFT))
+
+static u32 osc_ctrl_ctx;
 
 int __init tegra_osc_clk_init(void __iomem *clk_base, struct tegra_clk *clks,
 			      unsigned long *input_freqs, unsigned int num,
@@ -29,6 +33,7 @@ int __init tegra_osc_clk_init(void __iomem *clk_base, struct tegra_clk *clks,
 	unsigned osc_idx;
 
 	val = readl_relaxed(clk_base + OSC_CTRL);
+	osc_ctrl_ctx = val & OSC_CTRL_MASK;
 	osc_idx = val >> OSC_CTRL_OSC_FREQ_SHIFT;
 
 	if (osc_idx < num)
@@ -96,3 +101,13 @@ void __init tegra_fixed_clk_init(struct tegra_clk *tegra_clks)
 		*dt_clk = clk;
 	}
 }
+
+void tegra_clk_osc_resume(void __iomem *clk_base)
+{
+	u32 val;
+
+	val = readl_relaxed(clk_base + OSC_CTRL) & ~OSC_CTRL_MASK;
+	val |= osc_ctrl_ctx;
+	writel_relaxed(val, clk_base + OSC_CTRL);
+	fence_udelay(2, clk_base);
+}
diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
index f1ef6ae8c979..abba6d8a04cd 100644
--- a/drivers/clk/tegra/clk.h
+++ b/drivers/clk/tegra/clk.h
@@ -842,6 +842,7 @@ u16 tegra_pll_get_fixed_mdiv(struct clk_hw *hw, unsigned long input_rate);
 int tegra_pll_p_div_to_hw(struct tegra_clk_pll *pll, u8 p_div);
 int div_frac_get(unsigned long rate, unsigned parent_rate, u8 width,
 		 u8 frac_width, u8 flags);
+void tegra_clk_osc_resume(void __iomem *clk_base);
 
 
 /* Combined read fence with delay */
-- 
2.7.4

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

end of thread, other threads:[~2019-08-04 12:31 UTC | newest]

Thread overview: 67+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-07-31  0:20 [PATCH v7 00/20] SC7 entry and exit support for Tegra210 Sowjanya Komatineni
2019-07-31  0:20 ` [PATCH v7 01/20] pinctrl: tegra: Add suspend and resume support Sowjanya Komatineni
2019-07-31  0:20 ` [PATCH v7 02/20] pinctrl: tegra210: Add Tegra210 pinctrl pm ops Sowjanya Komatineni
2019-07-31  0:20 ` [PATCH v7 03/20] clk: tegra: divider: Save and restore divider rate Sowjanya Komatineni
2019-07-31 10:49   ` Dmitry Osipenko
2019-07-31  0:20 ` [PATCH v7 04/20] clk: tegra: pllout: Save and restore pllout context Sowjanya Komatineni
2019-07-31  0:20 ` [PATCH v7 05/20] clk: tegra: pll: Save and restore pll context Sowjanya Komatineni
2019-07-31  0:20 ` [PATCH v7 06/20] clk: tegra: Support for OSC context save and restore Sowjanya Komatineni
2019-07-31 11:11   ` Dmitry Osipenko
2019-07-31 21:04     ` Sowjanya Komatineni
2019-08-01 10:53       ` Dmitry Osipenko
2019-08-01 18:06         ` Sowjanya Komatineni
2019-08-01 18:42           ` Dmitry Osipenko
2019-07-31  0:20 ` [PATCH v7 07/20] clk: tegra: clk-periph: Add save and restore support Sowjanya Komatineni
2019-07-31  9:50   ` Dmitry Osipenko
2019-07-31 10:44     ` Dmitry Osipenko
2019-07-31 23:09       ` Sowjanya Komatineni
2019-08-01 17:58         ` Sowjanya Komatineni
2019-08-01 19:00           ` Dmitry Osipenko
2019-08-01 19:42             ` Sowjanya Komatineni
2019-08-01 20:17               ` Dmitry Osipenko
2019-08-01 20:31                 ` Sowjanya Komatineni
2019-08-01 20:54                   ` Dmitry Osipenko
2019-08-01 21:30                     ` Sowjanya Komatineni
2019-08-01 23:19                       ` Sowjanya Komatineni
2019-08-01 23:49                         ` Sowjanya Komatineni
2019-08-02 12:38                           ` Dmitry Osipenko
2019-08-02 18:33                             ` Sowjanya Komatineni
2019-08-02 20:13                               ` Dmitry Osipenko
2019-08-02 20:17                                 ` Dmitry Osipenko
2019-08-02 20:32                                   ` Sowjanya Komatineni
2019-08-02 21:15                                     ` Dmitry Osipenko
2019-08-02 21:18                                       ` Sowjanya Komatineni
2019-08-02 23:51                                       ` Sowjanya Komatineni
2019-08-03 10:33                                         ` Dmitry Osipenko
2019-08-03 17:01                                           ` Sowjanya Komatineni
2019-08-03 23:44                                             ` Sowjanya Komatineni
2019-08-04 12:24                                               ` Dmitry Osipenko
2019-08-04 12:31                                                 ` Dmitry Osipenko
2019-08-02 12:32   ` Dmitry Osipenko
2019-08-02 18:43     ` Sowjanya Komatineni
2019-08-02 20:20       ` Dmitry Osipenko
2019-08-02 20:37         ` Sowjanya Komatineni
2019-07-31  0:20 ` [PATCH v7 08/20] clk: tegra: clk-super: Fix to enable PLLP branches to CPU Sowjanya Komatineni
2019-07-31 10:14   ` Dmitry Osipenko
2019-07-31  0:20 ` [PATCH v7 09/20] clk: tegra: clk-super: Add save and restore support Sowjanya Komatineni
2019-07-31  0:20 ` [PATCH v7 10/20] clk: tegra: clk-dfll: Add suspend and resume support Sowjanya Komatineni
2019-07-31 10:12   ` Dmitry Osipenko
2019-07-31  0:20 ` [PATCH v7 11/20] cpufreq: tegra124: " Sowjanya Komatineni
2019-07-31 10:23   ` Dmitry Osipenko
2019-07-31 11:14     ` Dmitry Osipenko
2019-07-31 21:05       ` Sowjanya Komatineni
2019-07-31  0:20 ` [PATCH v7 12/20] clk: tegra210: Use fence_udelay during PLLU init Sowjanya Komatineni
2019-07-31  0:20 ` [PATCH v7 13/20] clk: tegra210: Add suspend and resume support Sowjanya Komatineni
2019-07-31  0:20 ` [PATCH v7 14/20] soc/tegra: pmc: Allow to support more tegras wake Sowjanya Komatineni
2019-07-31  0:20 ` [PATCH v7 15/20] soc/tegra: pmc: Add pmc wake support for tegra210 Sowjanya Komatineni
2019-07-31  0:20 ` [PATCH v7 16/20] arm64: tegra: Enable wake from deep sleep on RTC alarm Sowjanya Komatineni
2019-07-31 11:04   ` Dmitry Osipenko
2019-07-31 21:08     ` Sowjanya Komatineni
2019-08-01 10:43       ` Dmitry Osipenko
2019-08-01 17:56         ` Sowjanya Komatineni
2019-08-01 18:39           ` Dmitry Osipenko
2019-07-31  0:20 ` [PATCH v7 17/20] soc/tegra: pmc: Configure core power request polarity Sowjanya Komatineni
2019-07-31  0:20 ` [PATCH v7 18/20] soc/tegra: pmc: Configure deep sleep control settings Sowjanya Komatineni
2019-07-31  0:20 ` [PATCH v7 19/20] arm64: dts: tegra210-p2180: Jetson TX1 SC7 timings Sowjanya Komatineni
2019-07-31  0:20 ` [PATCH v7 20/20] arm64: dts: tegra210-p3450: Jetson Nano " Sowjanya Komatineni
2019-07-31 21:10 [PATCH v7 00/20] SC7 entry and exit support for Tegra210 Sowjanya Komatineni
2019-07-31 21:10 ` [PATCH v7 06/20] clk: tegra: Support for OSC context save and restore Sowjanya Komatineni

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).