Commit d6e7bbc1 authored by Russ Dill's avatar Russ Dill Committed by Tero Kristo

clk: ti: Add functions to save/restore clk context

SoCs like AM43XX lose clock registers context during RTC-only
suspend. Hence add functions to save/restore the clock registers
context.
Signed-off-by: default avatarKeerthy <j-keerthy@ti.com>
Signed-off-by: default avatarRuss Dill <Russ.Dill@ti.com>
Acked-by: default avatarTony Lindgren <tony@atomide.com>
Signed-off-by: default avatarTero Kristo <t-kristo@ti.com>
parent 43536548
......@@ -24,6 +24,7 @@ struct clk_omap_divider {
u8 flags;
s8 latch;
const struct clk_div_table *table;
u32 context;
};
#define to_clk_omap_divider(_hw) container_of(_hw, struct clk_omap_divider, hw)
......@@ -36,6 +37,7 @@ struct clk_omap_mux {
u8 shift;
s8 latch;
u8 flags;
u8 saved_parent;
};
#define to_clk_omap_mux(_hw) container_of(_hw, struct clk_omap_mux, hw)
......
......@@ -268,10 +268,46 @@ static int ti_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
return 0;
}
/**
* clk_divider_save_context - Save the divider value
* @hw: pointer struct clk_hw
*
* Save the divider value
*/
static int clk_divider_save_context(struct clk_hw *hw)
{
struct clk_omap_divider *divider = to_clk_omap_divider(hw);
u32 val;
val = ti_clk_ll_ops->clk_readl(&divider->reg) >> divider->shift;
divider->context = val & div_mask(divider);
return 0;
}
/**
* clk_divider_restore_context - restore the saved the divider value
* @hw: pointer struct clk_hw
*
* Restore the saved the divider value
*/
static void clk_divider_restore_context(struct clk_hw *hw)
{
struct clk_omap_divider *divider = to_clk_omap_divider(hw);
u32 val;
val = ti_clk_ll_ops->clk_readl(&divider->reg);
val &= ~(div_mask(divider) << divider->shift);
val |= divider->context << divider->shift;
ti_clk_ll_ops->clk_writel(val, &divider->reg);
}
const struct clk_ops ti_clk_divider_ops = {
.recalc_rate = ti_clk_divider_recalc_rate,
.round_rate = ti_clk_divider_round_rate,
.set_rate = ti_clk_divider_set_rate,
.save_context = clk_divider_save_context,
.restore_context = clk_divider_restore_context,
};
static struct clk *_register_divider(struct device *dev, const char *name,
......
......@@ -39,6 +39,8 @@ static const struct clk_ops dpll_m4xen_ck_ops = {
.set_rate_and_parent = &omap3_noncore_dpll_set_rate_and_parent,
.determine_rate = &omap4_dpll_regm4xen_determine_rate,
.get_parent = &omap2_init_dpll_parent,
.save_context = &omap3_core_dpll_save_context,
.restore_context = &omap3_core_dpll_restore_context,
};
#else
static const struct clk_ops dpll_m4xen_ck_ops = {};
......@@ -62,6 +64,8 @@ static const struct clk_ops dpll_ck_ops = {
.set_rate_and_parent = &omap3_noncore_dpll_set_rate_and_parent,
.determine_rate = &omap3_noncore_dpll_determine_rate,
.get_parent = &omap2_init_dpll_parent,
.save_context = &omap3_noncore_dpll_save_context,
.restore_context = &omap3_noncore_dpll_restore_context,
};
static const struct clk_ops dpll_no_gate_ck_ops = {
......@@ -72,6 +76,8 @@ static const struct clk_ops dpll_no_gate_ck_ops = {
.set_parent = &omap3_noncore_dpll_set_parent,
.set_rate_and_parent = &omap3_noncore_dpll_set_rate_and_parent,
.determine_rate = &omap3_noncore_dpll_determine_rate,
.save_context = &omap3_noncore_dpll_save_context,
.restore_context = &omap3_noncore_dpll_restore_context
};
#else
static const struct clk_ops dpll_core_ck_ops = {};
......
......@@ -782,6 +782,130 @@ unsigned long omap3_clkoutx2_recalc(struct clk_hw *hw,
return rate;
}
/**
* omap3_core_dpll_save_context - Save the m and n values of the divider
* @hw: pointer struct clk_hw
*
* Before the dpll registers are lost save the last rounded rate m and n
* and the enable mask.
*/
int omap3_core_dpll_save_context(struct clk_hw *hw)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
struct dpll_data *dd;
u32 v;
dd = clk->dpll_data;
v = ti_clk_ll_ops->clk_readl(&dd->control_reg);
clk->context = (v & dd->enable_mask) >> __ffs(dd->enable_mask);
if (clk->context == DPLL_LOCKED) {
v = ti_clk_ll_ops->clk_readl(&dd->mult_div1_reg);
dd->last_rounded_m = (v & dd->mult_mask) >>
__ffs(dd->mult_mask);
dd->last_rounded_n = ((v & dd->div1_mask) >>
__ffs(dd->div1_mask)) + 1;
}
return 0;
}
/**
* omap3_core_dpll_restore_context - restore the m and n values of the divider
* @hw: pointer struct clk_hw
*
* Restore the last rounded rate m and n
* and the enable mask.
*/
void omap3_core_dpll_restore_context(struct clk_hw *hw)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
const struct dpll_data *dd;
u32 v;
dd = clk->dpll_data;
if (clk->context == DPLL_LOCKED) {
_omap3_dpll_write_clken(clk, 0x4);
_omap3_wait_dpll_status(clk, 0);
v = ti_clk_ll_ops->clk_readl(&dd->mult_div1_reg);
v &= ~(dd->mult_mask | dd->div1_mask);
v |= dd->last_rounded_m << __ffs(dd->mult_mask);
v |= (dd->last_rounded_n - 1) << __ffs(dd->div1_mask);
ti_clk_ll_ops->clk_writel(v, &dd->mult_div1_reg);
_omap3_dpll_write_clken(clk, DPLL_LOCKED);
_omap3_wait_dpll_status(clk, 1);
} else {
_omap3_dpll_write_clken(clk, clk->context);
}
}
/**
* omap3_non_core_dpll_save_context - Save the m and n values of the divider
* @hw: pointer struct clk_hw
*
* Before the dpll registers are lost save the last rounded rate m and n
* and the enable mask.
*/
int omap3_noncore_dpll_save_context(struct clk_hw *hw)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
struct dpll_data *dd;
u32 v;
dd = clk->dpll_data;
v = ti_clk_ll_ops->clk_readl(&dd->control_reg);
clk->context = (v & dd->enable_mask) >> __ffs(dd->enable_mask);
if (clk->context == DPLL_LOCKED) {
v = ti_clk_ll_ops->clk_readl(&dd->mult_div1_reg);
dd->last_rounded_m = (v & dd->mult_mask) >>
__ffs(dd->mult_mask);
dd->last_rounded_n = ((v & dd->div1_mask) >>
__ffs(dd->div1_mask)) + 1;
}
return 0;
}
/**
* omap3_core_dpll_restore_context - restore the m and n values of the divider
* @hw: pointer struct clk_hw
*
* Restore the last rounded rate m and n
* and the enable mask.
*/
void omap3_noncore_dpll_restore_context(struct clk_hw *hw)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
const struct dpll_data *dd;
u32 ctrl, mult_div1;
dd = clk->dpll_data;
ctrl = ti_clk_ll_ops->clk_readl(&dd->control_reg);
mult_div1 = ti_clk_ll_ops->clk_readl(&dd->mult_div1_reg);
if (clk->context == ((ctrl & dd->enable_mask) >>
__ffs(dd->enable_mask)) &&
dd->last_rounded_m == ((mult_div1 & dd->mult_mask) >>
__ffs(dd->mult_mask)) &&
dd->last_rounded_n == ((mult_div1 & dd->div1_mask) >>
__ffs(dd->div1_mask)) + 1) {
/* nothing to be done */
return;
}
if (clk->context == DPLL_LOCKED)
omap3_noncore_dpll_program(clk, 0);
else
_omap3_dpll_write_clken(clk, clk->context);
}
/* OMAP3/4 non-CORE DPLL clkops */
const struct clk_hw_omap_ops clkhwops_omap3_dpll = {
.allow_idle = omap3_dpll_allow_idle,
......
......@@ -33,6 +33,7 @@ static const struct clk_ops omap_gate_clkdm_clk_ops = {
.init = &omap2_init_clk_clkdm,
.enable = &omap2_clkops_enable_clkdm,
.disable = &omap2_clkops_disable_clkdm,
.restore_context = clk_gate_restore_context,
};
const struct clk_ops omap_gate_clk_ops = {
......@@ -40,6 +41,7 @@ const struct clk_ops omap_gate_clk_ops = {
.enable = &omap2_dflt_clk_enable,
.disable = &omap2_dflt_clk_disable,
.is_enabled = &omap2_dflt_clk_is_enabled,
.restore_context = clk_gate_restore_context,
};
static const struct clk_ops omap_gate_clk_hsdiv_restore_ops = {
......@@ -47,6 +49,7 @@ static const struct clk_ops omap_gate_clk_hsdiv_restore_ops = {
.enable = &omap36xx_gate_clk_enable_with_hsdiv_restore,
.disable = &omap2_dflt_clk_disable,
.is_enabled = &omap2_dflt_clk_is_enabled,
.restore_context = clk_gate_restore_context,
};
/**
......
......@@ -91,10 +91,39 @@ static int ti_clk_mux_set_parent(struct clk_hw *hw, u8 index)
return 0;
}
/**
* clk_mux_save_context - Save the parent selcted in the mux
* @hw: pointer struct clk_hw
*
* Save the parent mux value.
*/
static int clk_mux_save_context(struct clk_hw *hw)
{
struct clk_omap_mux *mux = to_clk_omap_mux(hw);
mux->saved_parent = ti_clk_mux_get_parent(hw);
return 0;
}
/**
* clk_mux_restore_context - Restore the parent in the mux
* @hw: pointer struct clk_hw
*
* Restore the saved parent mux value.
*/
static void clk_mux_restore_context(struct clk_hw *hw)
{
struct clk_omap_mux *mux = to_clk_omap_mux(hw);
ti_clk_mux_set_parent(hw, mux->saved_parent);
}
const struct clk_ops ti_clk_mux_ops = {
.get_parent = ti_clk_mux_get_parent,
.set_parent = ti_clk_mux_set_parent,
.determine_rate = __clk_mux_determine_rate,
.save_context = clk_mux_save_context,
.restore_context = clk_mux_restore_context,
};
static struct clk *_register_mux(struct device *dev, const char *name,
......
......@@ -159,6 +159,7 @@ struct clk_hw_omap {
const char *clkdm_name;
struct clockdomain *clkdm;
const struct clk_hw_omap_ops *ops;
u32 context;
};
/*
......@@ -294,6 +295,11 @@ struct ti_clk_features {
void ti_clk_setup_features(struct ti_clk_features *features);
const struct ti_clk_features *ti_clk_get_features(void);
int omap3_noncore_dpll_save_context(struct clk_hw *hw);
void omap3_noncore_dpll_restore_context(struct clk_hw *hw);
int omap3_core_dpll_save_context(struct clk_hw *hw);
void omap3_core_dpll_restore_context(struct clk_hw *hw);
extern const struct clk_hw_omap_ops clkhwops_omap2xxx_dpll;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment