@@ -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,
@@ -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,
@@ -5,6 +5,7 @@
#include <linux/clk-provider.h>
#include <linux/export.h>
+#include <linux/io.h>
#include <linux/slab.h>
#include <linux/err.h>
@@ -99,6 +100,42 @@ 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;
+ struct clk_hw *parent = clk_hw_get_parent(hw);
+ unsigned long parent_rate = clk_hw_get_rate(parent);
+
+ if (!(periph->gate.flags & TEGRA_PERIPH_NO_GATE))
+ gate_ops->save_context(gate_hw);
+
+ if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
+ periph->rate_ctx = clk_periph_recalc_rate(hw, parent_rate);
+
+ 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;
+ struct clk_hw *parent = clk_hw_get_parent(hw);
+ unsigned long parent_rate = clk_hw_get_rate(parent);
+
+ clk_periph_set_parent(hw, periph->parent_ctx);
+
+ if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
+ clk_periph_set_rate(hw, periph->rate_ctx, parent_rate);
+
+ 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 +145,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 +155,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 +165,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,
@@ -194,6 +194,34 @@ 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;
+ struct clk_hw *parent = clk_hw_get_parent(hw);
+ unsigned long parent_rate = clk_hw_get_rate(parent);
+
+ sdmmc_mux->rate_ctx = clk_sdmmc_mux_recalc_rate(hw, parent_rate);
+ 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);
+
+ clk_sdmmc_mux_set_parent(hw, sdmmc_mux->parent_ctx);
+ clk_sdmmc_mux_set_rate(hw, sdmmc_mux->rate_ctx, 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 +231,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,
@@ -523,6 +523,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) \
@@ -549,6 +551,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,
@@ -581,6 +585,8 @@ struct tegra_clk_periph {
const struct clk_ops *mux_ops;
const struct clk_ops *div_ops;
const struct clk_ops *gate_ops;
+ unsigned long rate_ctx;
+ u8 parent_ctx;
};
#define to_clk_periph(_hw) container_of(_hw, struct tegra_clk_periph, hw)
@@ -732,6 +738,8 @@ struct tegra_sdmmc_mux {
const struct clk_ops *gate_ops;
struct tegra_clk_periph_gate gate;
u8 div_flags;
+ unsigned long rate_ctx;
+ u8 parent_ctx;
};
#define to_clk_sdmmc_mux(_hw) container_of(_hw, struct tegra_sdmmc_mux, hw)