01.08.2019 20:58, Sowjanya Komatineni ÐÐÑÐÑ:
On 7/31/19 4:09 PM, Sowjanya Komatineni wrote:Sounds like a good idea. I see that there is a 'restoring' helper for
On 7/31/19 3:44 AM, Dmitry Osipenko wrote:I hope its ok to add save/restore context to clk_mux_ops to be more
31.07.2019 12:50, Dmitry Osipenko ÐÐÑÐÑ:clk_periph_get_parent basically invokes existing clk_mux_ops
31.07.2019 3:20, Sowjanya Komatineni ÐÐÑÐÑ:Ah, I now see that there is no need to save the dividers context because
This patch implements save and restore context for peripheral fixedCould you please point to where the divider's save_context() happens?
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@xxxxxxxxxx>
Signed-off-by: Sowjanya Komatineni <skomatineni@xxxxxxxxxx>
---
 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);
Because I can't see it.
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.
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.
generic w.r.t save/restore context rather than get_parent_index API.
Please confirm if you agree.
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.