Skip to content

Commit

Permalink
clk: at91: re-factor clocks suspend/resume
Browse files Browse the repository at this point in the history
SAMA5D2 and SAMA7G5 have a special power saving mode (backup mode) where
most of the SoC's components are powered off (including PMC). Resuming
from this mode is done with the help of bootloader. Peripherals are not
aware of the power saving mode thus most of them are disabling clocks in
proper suspend API and re-enable them in resume API without taking into
account the previously setup rate. Moreover some of the peripherals are
acting as wakeup sources and are not disabling the clocks in this
scenario, when suspending. Since backup mode cuts the power for
peripherals, in resume part these clocks needs to be re-configured.

The initial PMC suspend/resume code was designed only for SAMA5D2's PMC
(as it was the only one supporting backup mode). SAMA7G supports also
backup mode and its PMC is different (few new functionalities, different
registers offsets, different offsets in registers for each
functionalities). To address both SAMA5D2 and SAMA7G5 PMC add
.save_context()/.resume_context() support to each clocks driver and call
this from PMC driver.

Signed-off-by: Claudiu Beznea <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Acked-by: Nicolas Ferre <[email protected]>
Signed-off-by: Stephen Boyd <[email protected]>
  • Loading branch information
claudiubeznea authored and bebarino committed Oct 27, 2021
1 parent c405f5c commit 3697156
Show file tree
Hide file tree
Showing 12 changed files with 558 additions and 181 deletions.
46 changes: 37 additions & 9 deletions drivers/clk/at91/clk-generated.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,32 +27,43 @@ struct clk_generated {
u32 id;
u32 gckdiv;
const struct clk_pcr_layout *layout;
struct at91_clk_pms pms;
u8 parent_id;
int chg_pid;
};

#define to_clk_generated(hw) \
container_of(hw, struct clk_generated, hw)

static int clk_generated_enable(struct clk_hw *hw)
static int clk_generated_set(struct clk_generated *gck, int status)
{
struct clk_generated *gck = to_clk_generated(hw);
unsigned long flags;

pr_debug("GCLK: %s, gckdiv = %d, parent id = %d\n",
__func__, gck->gckdiv, gck->parent_id);
unsigned int enable = status ? AT91_PMC_PCR_GCKEN : 0;

spin_lock_irqsave(gck->lock, flags);
regmap_write(gck->regmap, gck->layout->offset,
(gck->id & gck->layout->pid_mask));
regmap_update_bits(gck->regmap, gck->layout->offset,
AT91_PMC_PCR_GCKDIV_MASK | gck->layout->gckcss_mask |
gck->layout->cmd | AT91_PMC_PCR_GCKEN,
gck->layout->cmd | enable,
field_prep(gck->layout->gckcss_mask, gck->parent_id) |
gck->layout->cmd |
FIELD_PREP(AT91_PMC_PCR_GCKDIV_MASK, gck->gckdiv) |
AT91_PMC_PCR_GCKEN);
enable);
spin_unlock_irqrestore(gck->lock, flags);

return 0;
}

static int clk_generated_enable(struct clk_hw *hw)
{
struct clk_generated *gck = to_clk_generated(hw);

pr_debug("GCLK: %s, gckdiv = %d, parent id = %d\n",
__func__, gck->gckdiv, gck->parent_id);

clk_generated_set(gck, 1);

return 0;
}

Expand Down Expand Up @@ -245,6 +256,23 @@ static int clk_generated_set_rate(struct clk_hw *hw,
return 0;
}

static int clk_generated_save_context(struct clk_hw *hw)
{
struct clk_generated *gck = to_clk_generated(hw);

gck->pms.status = clk_generated_is_enabled(&gck->hw);

return 0;
}

static void clk_generated_restore_context(struct clk_hw *hw)
{
struct clk_generated *gck = to_clk_generated(hw);

if (gck->pms.status)
clk_generated_set(gck, gck->pms.status);
}

static const struct clk_ops generated_ops = {
.enable = clk_generated_enable,
.disable = clk_generated_disable,
Expand All @@ -254,6 +282,8 @@ static const struct clk_ops generated_ops = {
.get_parent = clk_generated_get_parent,
.set_parent = clk_generated_set_parent,
.set_rate = clk_generated_set_rate,
.save_context = clk_generated_save_context,
.restore_context = clk_generated_restore_context,
};

/**
Expand Down Expand Up @@ -320,8 +350,6 @@ at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
if (ret) {
kfree(gck);
hw = ERR_PTR(ret);
} else {
pmc_register_id(id);
}

return hw;
Expand Down
66 changes: 66 additions & 0 deletions drivers/clk/at91/clk-main.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
struct clk_main_osc {
struct clk_hw hw;
struct regmap *regmap;
struct at91_clk_pms pms;
};

#define to_clk_main_osc(hw) container_of(hw, struct clk_main_osc, hw)
Expand All @@ -37,6 +38,7 @@ struct clk_main_rc_osc {
struct regmap *regmap;
unsigned long frequency;
unsigned long accuracy;
struct at91_clk_pms pms;
};

#define to_clk_main_rc_osc(hw) container_of(hw, struct clk_main_rc_osc, hw)
Expand All @@ -51,6 +53,7 @@ struct clk_rm9200_main {
struct clk_sam9x5_main {
struct clk_hw hw;
struct regmap *regmap;
struct at91_clk_pms pms;
u8 parent;
};

Expand Down Expand Up @@ -120,10 +123,29 @@ static int clk_main_osc_is_prepared(struct clk_hw *hw)
return (status & AT91_PMC_MOSCS) && clk_main_parent_select(tmp);
}

static int clk_main_osc_save_context(struct clk_hw *hw)
{
struct clk_main_osc *osc = to_clk_main_osc(hw);

osc->pms.status = clk_main_osc_is_prepared(hw);

return 0;
}

static void clk_main_osc_restore_context(struct clk_hw *hw)
{
struct clk_main_osc *osc = to_clk_main_osc(hw);

if (osc->pms.status)
clk_main_osc_prepare(hw);
}

static const struct clk_ops main_osc_ops = {
.prepare = clk_main_osc_prepare,
.unprepare = clk_main_osc_unprepare,
.is_prepared = clk_main_osc_is_prepared,
.save_context = clk_main_osc_save_context,
.restore_context = clk_main_osc_restore_context,
};

struct clk_hw * __init
Expand Down Expand Up @@ -240,12 +262,31 @@ static unsigned long clk_main_rc_osc_recalc_accuracy(struct clk_hw *hw,
return osc->accuracy;
}

static int clk_main_rc_osc_save_context(struct clk_hw *hw)
{
struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw);

osc->pms.status = clk_main_rc_osc_is_prepared(hw);

return 0;
}

static void clk_main_rc_osc_restore_context(struct clk_hw *hw)
{
struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw);

if (osc->pms.status)
clk_main_rc_osc_prepare(hw);
}

static const struct clk_ops main_rc_osc_ops = {
.prepare = clk_main_rc_osc_prepare,
.unprepare = clk_main_rc_osc_unprepare,
.is_prepared = clk_main_rc_osc_is_prepared,
.recalc_rate = clk_main_rc_osc_recalc_rate,
.recalc_accuracy = clk_main_rc_osc_recalc_accuracy,
.save_context = clk_main_rc_osc_save_context,
.restore_context = clk_main_rc_osc_restore_context,
};

struct clk_hw * __init
Expand Down Expand Up @@ -465,12 +506,37 @@ static u8 clk_sam9x5_main_get_parent(struct clk_hw *hw)
return clk_main_parent_select(status);
}

static int clk_sam9x5_main_save_context(struct clk_hw *hw)
{
struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw);

clkmain->pms.status = clk_main_rc_osc_is_prepared(&clkmain->hw);
clkmain->pms.parent = clk_sam9x5_main_get_parent(&clkmain->hw);

return 0;
}

static void clk_sam9x5_main_restore_context(struct clk_hw *hw)
{
struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw);
int ret;

ret = clk_sam9x5_main_set_parent(hw, clkmain->pms.parent);
if (ret)
return;

if (clkmain->pms.status)
clk_sam9x5_main_prepare(hw);
}

static const struct clk_ops sam9x5_main_ops = {
.prepare = clk_sam9x5_main_prepare,
.is_prepared = clk_sam9x5_main_is_prepared,
.recalc_rate = clk_sam9x5_main_recalc_rate,
.set_parent = clk_sam9x5_main_set_parent,
.get_parent = clk_sam9x5_main_get_parent,
.save_context = clk_sam9x5_main_save_context,
.restore_context = clk_sam9x5_main_restore_context,
};

struct clk_hw * __init
Expand Down
Loading

0 comments on commit 3697156

Please sign in to comment.