Skip to content

Commit

Permalink
clk: ti: Add functions to save/restore clk context
Browse files Browse the repository at this point in the history
SoCs like AM43XX lose clock registers context during RTC-only
suspend. Hence add functions to save/restore the clock registers
context.

Signed-off-by: Keerthy <j-keerthy@ti.com>
Signed-off-by: Russ Dill <Russ.Dill@ti.com>
Acked-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Tero Kristo <t-kristo@ti.com>
  • Loading branch information
Russ Dill authored and Tero Kristo committed Oct 3, 2018
1 parent 4353654 commit d6e7bbc
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 0 deletions.
2 changes: 2 additions & 0 deletions drivers/clk/ti/clock.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down
36 changes: 36 additions & 0 deletions drivers/clk/ti/divider.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions drivers/clk/ti/dpll.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {};
Expand All @@ -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 = {
Expand All @@ -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 = {};
Expand Down
124 changes: 124 additions & 0 deletions drivers/clk/ti/dpll3xxx.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
3 changes: 3 additions & 0 deletions drivers/clk/ti/gate.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,23 @@ 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 = {
.init = &omap2_init_clk_clkdm,
.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 = {
.init = &omap2_init_clk_clkdm,
.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,
};

/**
Expand Down
29 changes: 29 additions & 0 deletions drivers/clk/ti/mux.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions include/linux/clk/ti.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ struct clk_hw_omap {
const char *clkdm_name;
struct clockdomain *clkdm;
const struct clk_hw_omap_ops *ops;
u32 context;
};

/*
Expand Down Expand Up @@ -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;

Expand Down

0 comments on commit d6e7bbc

Please sign in to comment.