X-Git-Url: http://git.openwrt.org/?p=openwrt%2Fopenwrt.git;a=blobdiff_plain;f=target%2Flinux%2Fat91%2Fpatches-5.15%2F234-clk-at91-re-factor-clocks-suspend-resume.patch;fp=target%2Flinux%2Fat91%2Fpatches-5.15%2F234-clk-at91-re-factor-clocks-suspend-resume.patch;h=5d399f6535e8ee79d1422123de00c33c781a3c49;hp=0000000000000000000000000000000000000000;hb=eb758a8fec2cee24e528008052fa2bd58482ca3a;hpb=c5c37886cff1705ba9be9b33df3ab121bd27fe6b diff --git a/target/linux/at91/patches-5.15/234-clk-at91-re-factor-clocks-suspend-resume.patch b/target/linux/at91/patches-5.15/234-clk-at91-re-factor-clocks-suspend-resume.patch new file mode 100644 index 0000000000..5d399f6535 --- /dev/null +++ b/target/linux/at91/patches-5.15/234-clk-at91-re-factor-clocks-suspend-resume.patch @@ -0,0 +1,1342 @@ +From 65bb4687b2a5c6f02f44345540c3389d6e7523e7 Mon Sep 17 00:00:00 2001 +From: Claudiu Beznea +Date: Mon, 11 Oct 2021 14:27:05 +0300 +Subject: [PATCH 234/247] clk: at91: re-factor clocks suspend/resume + +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 +Link: https://lore.kernel.org/r/20211011112719.3951784-2-claudiu.beznea@microchip.com +Acked-by: Nicolas Ferre +Signed-off-by: Stephen Boyd +--- + drivers/clk/at91/clk-generated.c | 46 +++++-- + drivers/clk/at91/clk-main.c | 66 ++++++++++ + drivers/clk/at91/clk-master.c | 194 ++++++++++++++++++++++++++-- + drivers/clk/at91/clk-peripheral.c | 40 +++++- + drivers/clk/at91/clk-pll.c | 39 ++++++ + drivers/clk/at91/clk-programmable.c | 29 ++++- + drivers/clk/at91/clk-sam9x60-pll.c | 68 +++++++++- + drivers/clk/at91/clk-system.c | 20 +++ + drivers/clk/at91/clk-usb.c | 27 ++++ + drivers/clk/at91/clk-utmi.c | 39 ++++++ + drivers/clk/at91/pmc.c | 147 +-------------------- + drivers/clk/at91/pmc.h | 24 ++-- + 12 files changed, 558 insertions(+), 181 deletions(-) + +--- a/drivers/clk/at91/clk-generated.c ++++ b/drivers/clk/at91/clk-generated.c +@@ -27,6 +27,7 @@ struct clk_generated { + u32 id; + u32 gckdiv; + const struct clk_pcr_layout *layout; ++ struct at91_clk_pms pms; + u8 parent_id; + int chg_pid; + }; +@@ -34,25 +35,35 @@ struct clk_generated { + #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; + } + +@@ -249,6 +260,23 @@ static int clk_generated_set_rate(struct + 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, +@@ -258,6 +286,8 @@ static const struct clk_ops generated_op + .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, + }; + + /** +@@ -324,8 +354,6 @@ at91_clk_register_generated(struct regma + if (ret) { + kfree(gck); + hw = ERR_PTR(ret); +- } else { +- pmc_register_id(id); + } + + return hw; +--- a/drivers/clk/at91/clk-main.c ++++ b/drivers/clk/at91/clk-main.c +@@ -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) +@@ -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) +@@ -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; + }; + +@@ -120,10 +123,29 @@ static int clk_main_osc_is_prepared(stru + 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 +@@ -240,12 +262,31 @@ static unsigned long clk_main_rc_osc_rec + 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 +@@ -465,12 +506,37 @@ static u8 clk_sam9x5_main_get_parent(str + 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 +--- a/drivers/clk/at91/clk-master.c ++++ b/drivers/clk/at91/clk-master.c +@@ -37,6 +37,7 @@ struct clk_master { + spinlock_t *lock; + const struct clk_master_layout *layout; + const struct clk_master_characteristics *characteristics; ++ struct at91_clk_pms pms; + u32 *mux_table; + u32 mckr; + int chg_pid; +@@ -112,10 +113,52 @@ static unsigned long clk_master_div_reca + return rate; + } + ++static int clk_master_div_save_context(struct clk_hw *hw) ++{ ++ struct clk_master *master = to_clk_master(hw); ++ struct clk_hw *parent_hw = clk_hw_get_parent(hw); ++ unsigned long flags; ++ unsigned int mckr, div; ++ ++ spin_lock_irqsave(master->lock, flags); ++ regmap_read(master->regmap, master->layout->offset, &mckr); ++ spin_unlock_irqrestore(master->lock, flags); ++ ++ mckr &= master->layout->mask; ++ div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK; ++ div = master->characteristics->divisors[div]; ++ ++ master->pms.parent_rate = clk_hw_get_rate(parent_hw); ++ master->pms.rate = DIV_ROUND_CLOSEST(master->pms.parent_rate, div); ++ ++ return 0; ++} ++ ++static void clk_master_div_restore_context(struct clk_hw *hw) ++{ ++ struct clk_master *master = to_clk_master(hw); ++ unsigned long flags; ++ unsigned int mckr; ++ u8 div; ++ ++ spin_lock_irqsave(master->lock, flags); ++ regmap_read(master->regmap, master->layout->offset, &mckr); ++ spin_unlock_irqrestore(master->lock, flags); ++ ++ mckr &= master->layout->mask; ++ div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK; ++ div = master->characteristics->divisors[div]; ++ ++ if (div != DIV_ROUND_CLOSEST(master->pms.parent_rate, master->pms.rate)) ++ pr_warn("MCKR DIV not configured properly by firmware!\n"); ++} ++ + static const struct clk_ops master_div_ops = { + .prepare = clk_master_prepare, + .is_prepared = clk_master_is_prepared, + .recalc_rate = clk_master_div_recalc_rate, ++ .save_context = clk_master_div_save_context, ++ .restore_context = clk_master_div_restore_context, + }; + + static int clk_master_div_set_rate(struct clk_hw *hw, unsigned long rate, +@@ -125,7 +168,9 @@ static int clk_master_div_set_rate(struc + const struct clk_master_characteristics *characteristics = + master->characteristics; + unsigned long flags; ++ unsigned int mckr, tmp; + int div, i; ++ int ret; + + div = DIV_ROUND_CLOSEST(parent_rate, rate); + if (div > ARRAY_SIZE(characteristics->divisors)) +@@ -145,11 +190,24 @@ static int clk_master_div_set_rate(struc + return -EINVAL; + + spin_lock_irqsave(master->lock, flags); +- regmap_update_bits(master->regmap, master->layout->offset, +- (MASTER_DIV_MASK << MASTER_DIV_SHIFT), +- (div << MASTER_DIV_SHIFT)); ++ ret = regmap_read(master->regmap, master->layout->offset, &mckr); ++ if (ret) ++ goto unlock; ++ ++ tmp = mckr & master->layout->mask; ++ tmp = (tmp >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK; ++ if (tmp == div) ++ goto unlock; ++ ++ mckr &= ~(MASTER_DIV_MASK << MASTER_DIV_SHIFT); ++ mckr |= (div << MASTER_DIV_SHIFT); ++ ret = regmap_write(master->regmap, master->layout->offset, mckr); ++ if (ret) ++ goto unlock; ++ + while (!clk_master_ready(master)) + cpu_relax(); ++unlock: + spin_unlock_irqrestore(master->lock, flags); + + return 0; +@@ -197,12 +255,25 @@ static int clk_master_div_determine_rate + return 0; + } + ++static void clk_master_div_restore_context_chg(struct clk_hw *hw) ++{ ++ struct clk_master *master = to_clk_master(hw); ++ int ret; ++ ++ ret = clk_master_div_set_rate(hw, master->pms.rate, ++ master->pms.parent_rate); ++ if (ret) ++ pr_warn("Failed to restore MCK DIV clock\n"); ++} ++ + static const struct clk_ops master_div_ops_chg = { + .prepare = clk_master_prepare, + .is_prepared = clk_master_is_prepared, + .recalc_rate = clk_master_div_recalc_rate, + .determine_rate = clk_master_div_determine_rate, + .set_rate = clk_master_div_set_rate, ++ .save_context = clk_master_div_save_context, ++ .restore_context = clk_master_div_restore_context_chg, + }; + + static void clk_sama7g5_master_best_diff(struct clk_rate_request *req, +@@ -272,7 +343,8 @@ static int clk_master_pres_set_rate(stru + { + struct clk_master *master = to_clk_master(hw); + unsigned long flags; +- unsigned int pres; ++ unsigned int pres, mckr, tmp; ++ int ret; + + pres = DIV_ROUND_CLOSEST(parent_rate, rate); + if (pres > MASTER_PRES_MAX) +@@ -284,15 +356,27 @@ static int clk_master_pres_set_rate(stru + pres = ffs(pres) - 1; + + spin_lock_irqsave(master->lock, flags); +- regmap_update_bits(master->regmap, master->layout->offset, +- (MASTER_PRES_MASK << master->layout->pres_shift), +- (pres << master->layout->pres_shift)); ++ ret = regmap_read(master->regmap, master->layout->offset, &mckr); ++ if (ret) ++ goto unlock; ++ ++ mckr &= master->layout->mask; ++ tmp = (mckr >> master->layout->pres_shift) & MASTER_PRES_MASK; ++ if (pres == tmp) ++ goto unlock; ++ ++ mckr &= ~(MASTER_PRES_MASK << master->layout->pres_shift); ++ mckr |= (pres << master->layout->pres_shift); ++ ret = regmap_write(master->regmap, master->layout->offset, mckr); ++ if (ret) ++ goto unlock; + + while (!clk_master_ready(master)) + cpu_relax(); ++unlock: + spin_unlock_irqrestore(master->lock, flags); + +- return 0; ++ return ret; + } + + static unsigned long clk_master_pres_recalc_rate(struct clk_hw *hw, +@@ -330,11 +414,68 @@ static u8 clk_master_pres_get_parent(str + return mckr & AT91_PMC_CSS; + } + ++static int clk_master_pres_save_context(struct clk_hw *hw) ++{ ++ struct clk_master *master = to_clk_master(hw); ++ struct clk_hw *parent_hw = clk_hw_get_parent(hw); ++ unsigned long flags; ++ unsigned int val, pres; ++ ++ spin_lock_irqsave(master->lock, flags); ++ regmap_read(master->regmap, master->layout->offset, &val); ++ spin_unlock_irqrestore(master->lock, flags); ++ ++ val &= master->layout->mask; ++ pres = (val >> master->layout->pres_shift) & MASTER_PRES_MASK; ++ if (pres == MASTER_PRES_MAX && master->characteristics->have_div3_pres) ++ pres = 3; ++ else ++ pres = (1 << pres); ++ ++ master->pms.parent = val & AT91_PMC_CSS; ++ master->pms.parent_rate = clk_hw_get_rate(parent_hw); ++ master->pms.rate = DIV_ROUND_CLOSEST_ULL(master->pms.parent_rate, pres); ++ ++ return 0; ++} ++ ++static void clk_master_pres_restore_context(struct clk_hw *hw) ++{ ++ struct clk_master *master = to_clk_master(hw); ++ unsigned long flags; ++ unsigned int val, pres; ++ ++ spin_lock_irqsave(master->lock, flags); ++ regmap_read(master->regmap, master->layout->offset, &val); ++ spin_unlock_irqrestore(master->lock, flags); ++ ++ val &= master->layout->mask; ++ pres = (val >> master->layout->pres_shift) & MASTER_PRES_MASK; ++ if (pres == MASTER_PRES_MAX && master->characteristics->have_div3_pres) ++ pres = 3; ++ else ++ pres = (1 << pres); ++ ++ if (master->pms.rate != ++ DIV_ROUND_CLOSEST_ULL(master->pms.parent_rate, pres) || ++ (master->pms.parent != (val & AT91_PMC_CSS))) ++ pr_warn("MCKR PRES was not configured properly by firmware!\n"); ++} ++ ++static void clk_master_pres_restore_context_chg(struct clk_hw *hw) ++{ ++ struct clk_master *master = to_clk_master(hw); ++ ++ clk_master_pres_set_rate(hw, master->pms.rate, master->pms.parent_rate); ++} ++ + static const struct clk_ops master_pres_ops = { + .prepare = clk_master_prepare, + .is_prepared = clk_master_is_prepared, + .recalc_rate = clk_master_pres_recalc_rate, + .get_parent = clk_master_pres_get_parent, ++ .save_context = clk_master_pres_save_context, ++ .restore_context = clk_master_pres_restore_context, + }; + + static const struct clk_ops master_pres_ops_chg = { +@@ -344,6 +485,8 @@ static const struct clk_ops master_pres_ + .recalc_rate = clk_master_pres_recalc_rate, + .get_parent = clk_master_pres_get_parent, + .set_rate = clk_master_pres_set_rate, ++ .save_context = clk_master_pres_save_context, ++ .restore_context = clk_master_pres_restore_context_chg, + }; + + static struct clk_hw * __init +@@ -539,20 +682,21 @@ static int clk_sama7g5_master_set_parent + return 0; + } + +-static int clk_sama7g5_master_enable(struct clk_hw *hw) ++static void clk_sama7g5_master_set(struct clk_master *master, ++ unsigned int status) + { +- struct clk_master *master = to_clk_master(hw); + unsigned long flags; + unsigned int val, cparent; ++ unsigned int enable = status ? PMC_MCR_EN : 0; + + spin_lock_irqsave(master->lock, flags); + + regmap_write(master->regmap, PMC_MCR, PMC_MCR_ID(master->id)); + regmap_read(master->regmap, PMC_MCR, &val); + regmap_update_bits(master->regmap, PMC_MCR, +- PMC_MCR_EN | PMC_MCR_CSS | PMC_MCR_DIV | ++ enable | PMC_MCR_CSS | PMC_MCR_DIV | + PMC_MCR_CMD | PMC_MCR_ID_MSK, +- PMC_MCR_EN | (master->parent << PMC_MCR_CSS_SHIFT) | ++ enable | (master->parent << PMC_MCR_CSS_SHIFT) | + (master->div << MASTER_DIV_SHIFT) | + PMC_MCR_CMD | PMC_MCR_ID(master->id)); + +@@ -563,6 +707,13 @@ static int clk_sama7g5_master_enable(str + cpu_relax(); + + spin_unlock_irqrestore(master->lock, flags); ++} ++ ++static int clk_sama7g5_master_enable(struct clk_hw *hw) ++{ ++ struct clk_master *master = to_clk_master(hw); ++ ++ clk_sama7g5_master_set(master, 1); + + return 0; + } +@@ -620,6 +771,23 @@ static int clk_sama7g5_master_set_rate(s + return 0; + } + ++static int clk_sama7g5_master_save_context(struct clk_hw *hw) ++{ ++ struct clk_master *master = to_clk_master(hw); ++ ++ master->pms.status = clk_sama7g5_master_is_enabled(hw); ++ ++ return 0; ++} ++ ++static void clk_sama7g5_master_restore_context(struct clk_hw *hw) ++{ ++ struct clk_master *master = to_clk_master(hw); ++ ++ if (master->pms.status) ++ clk_sama7g5_master_set(master, master->pms.status); ++} ++ + static const struct clk_ops sama7g5_master_ops = { + .enable = clk_sama7g5_master_enable, + .disable = clk_sama7g5_master_disable, +@@ -629,6 +797,8 @@ static const struct clk_ops sama7g5_mast + .set_rate = clk_sama7g5_master_set_rate, + .get_parent = clk_sama7g5_master_get_parent, + .set_parent = clk_sama7g5_master_set_parent, ++ .save_context = clk_sama7g5_master_save_context, ++ .restore_context = clk_sama7g5_master_restore_context, + }; + + struct clk_hw * __init +--- a/drivers/clk/at91/clk-peripheral.c ++++ b/drivers/clk/at91/clk-peripheral.c +@@ -37,6 +37,7 @@ struct clk_sam9x5_peripheral { + u32 id; + u32 div; + const struct clk_pcr_layout *layout; ++ struct at91_clk_pms pms; + bool auto_div; + int chg_pid; + }; +@@ -155,10 +156,11 @@ static void clk_sam9x5_peripheral_autodi + periph->div = shift; + } + +-static int clk_sam9x5_peripheral_enable(struct clk_hw *hw) ++static int clk_sam9x5_peripheral_set(struct clk_sam9x5_peripheral *periph, ++ unsigned int status) + { +- struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); + unsigned long flags; ++ unsigned int enable = status ? AT91_PMC_PCR_EN : 0; + + if (periph->id < PERIPHERAL_ID_MIN) + return 0; +@@ -168,15 +170,21 @@ static int clk_sam9x5_peripheral_enable( + (periph->id & periph->layout->pid_mask)); + regmap_update_bits(periph->regmap, periph->layout->offset, + periph->layout->div_mask | periph->layout->cmd | +- AT91_PMC_PCR_EN, ++ enable, + field_prep(periph->layout->div_mask, periph->div) | +- periph->layout->cmd | +- AT91_PMC_PCR_EN); ++ periph->layout->cmd | enable); + spin_unlock_irqrestore(periph->lock, flags); + + return 0; + } + ++static int clk_sam9x5_peripheral_enable(struct clk_hw *hw) ++{ ++ struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); ++ ++ return clk_sam9x5_peripheral_set(periph, 1); ++} ++ + static void clk_sam9x5_peripheral_disable(struct clk_hw *hw) + { + struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); +@@ -393,6 +401,23 @@ static int clk_sam9x5_peripheral_set_rat + return -EINVAL; + } + ++static int clk_sam9x5_peripheral_save_context(struct clk_hw *hw) ++{ ++ struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); ++ ++ periph->pms.status = clk_sam9x5_peripheral_is_enabled(hw); ++ ++ return 0; ++} ++ ++static void clk_sam9x5_peripheral_restore_context(struct clk_hw *hw) ++{ ++ struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); ++ ++ if (periph->pms.status) ++ clk_sam9x5_peripheral_set(periph, periph->pms.status); ++} ++ + static const struct clk_ops sam9x5_peripheral_ops = { + .enable = clk_sam9x5_peripheral_enable, + .disable = clk_sam9x5_peripheral_disable, +@@ -400,6 +425,8 @@ static const struct clk_ops sam9x5_perip + .recalc_rate = clk_sam9x5_peripheral_recalc_rate, + .round_rate = clk_sam9x5_peripheral_round_rate, + .set_rate = clk_sam9x5_peripheral_set_rate, ++ .save_context = clk_sam9x5_peripheral_save_context, ++ .restore_context = clk_sam9x5_peripheral_restore_context, + }; + + static const struct clk_ops sam9x5_peripheral_chg_ops = { +@@ -409,6 +436,8 @@ static const struct clk_ops sam9x5_perip + .recalc_rate = clk_sam9x5_peripheral_recalc_rate, + .determine_rate = clk_sam9x5_peripheral_determine_rate, + .set_rate = clk_sam9x5_peripheral_set_rate, ++ .save_context = clk_sam9x5_peripheral_save_context, ++ .restore_context = clk_sam9x5_peripheral_restore_context, + }; + + struct clk_hw * __init +@@ -460,7 +489,6 @@ at91_clk_register_sam9x5_peripheral(stru + hw = ERR_PTR(ret); + } else { + clk_sam9x5_peripheral_autodiv(periph); +- pmc_register_id(id); + } + + return hw; +--- a/drivers/clk/at91/clk-pll.c ++++ b/drivers/clk/at91/clk-pll.c +@@ -40,6 +40,7 @@ struct clk_pll { + u16 mul; + const struct clk_pll_layout *layout; + const struct clk_pll_characteristics *characteristics; ++ struct at91_clk_pms pms; + }; + + static inline bool clk_pll_ready(struct regmap *regmap, int id) +@@ -260,6 +261,42 @@ static int clk_pll_set_rate(struct clk_h + return 0; + } + ++static int clk_pll_save_context(struct clk_hw *hw) ++{ ++ struct clk_pll *pll = to_clk_pll(hw); ++ struct clk_hw *parent_hw = clk_hw_get_parent(hw); ++ ++ pll->pms.parent_rate = clk_hw_get_rate(parent_hw); ++ pll->pms.rate = clk_pll_recalc_rate(&pll->hw, pll->pms.parent_rate); ++ pll->pms.status = clk_pll_ready(pll->regmap, PLL_REG(pll->id)); ++ ++ return 0; ++} ++ ++static void clk_pll_restore_context(struct clk_hw *hw) ++{ ++ struct clk_pll *pll = to_clk_pll(hw); ++ unsigned long calc_rate; ++ unsigned int pllr, pllr_out, pllr_count; ++ u8 out = 0; ++ ++ if (pll->characteristics->out) ++ out = pll->characteristics->out[pll->range]; ++ ++ regmap_read(pll->regmap, PLL_REG(pll->id), &pllr); ++ ++ calc_rate = (pll->pms.parent_rate / PLL_DIV(pllr)) * ++ (PLL_MUL(pllr, pll->layout) + 1); ++ pllr_count = (pllr >> PLL_COUNT_SHIFT) & PLL_MAX_COUNT; ++ pllr_out = (pllr >> PLL_OUT_SHIFT) & out; ++ ++ if (pll->pms.rate != calc_rate || ++ pll->pms.status != clk_pll_ready(pll->regmap, PLL_REG(pll->id)) || ++ pllr_count != PLL_MAX_COUNT || ++ (out && pllr_out != out)) ++ pr_warn("PLLAR was not configured properly by firmware\n"); ++} ++ + static const struct clk_ops pll_ops = { + .prepare = clk_pll_prepare, + .unprepare = clk_pll_unprepare, +@@ -267,6 +304,8 @@ static const struct clk_ops pll_ops = { + .recalc_rate = clk_pll_recalc_rate, + .round_rate = clk_pll_round_rate, + .set_rate = clk_pll_set_rate, ++ .save_context = clk_pll_save_context, ++ .restore_context = clk_pll_restore_context, + }; + + struct clk_hw * __init +--- a/drivers/clk/at91/clk-programmable.c ++++ b/drivers/clk/at91/clk-programmable.c +@@ -24,6 +24,7 @@ struct clk_programmable { + u32 *mux_table; + u8 id; + const struct clk_programmable_layout *layout; ++ struct at91_clk_pms pms; + }; + + #define to_clk_programmable(hw) container_of(hw, struct clk_programmable, hw) +@@ -177,12 +178,38 @@ static int clk_programmable_set_rate(str + return 0; + } + ++static int clk_programmable_save_context(struct clk_hw *hw) ++{ ++ struct clk_programmable *prog = to_clk_programmable(hw); ++ struct clk_hw *parent_hw = clk_hw_get_parent(hw); ++ ++ prog->pms.parent = clk_programmable_get_parent(hw); ++ prog->pms.parent_rate = clk_hw_get_rate(parent_hw); ++ prog->pms.rate = clk_programmable_recalc_rate(hw, prog->pms.parent_rate); ++ ++ return 0; ++} ++ ++static void clk_programmable_restore_context(struct clk_hw *hw) ++{ ++ struct clk_programmable *prog = to_clk_programmable(hw); ++ int ret; ++ ++ ret = clk_programmable_set_parent(hw, prog->pms.parent); ++ if (ret) ++ return; ++ ++ clk_programmable_set_rate(hw, prog->pms.rate, prog->pms.parent_rate); ++} ++ + static const struct clk_ops programmable_ops = { + .recalc_rate = clk_programmable_recalc_rate, + .determine_rate = clk_programmable_determine_rate, + .get_parent = clk_programmable_get_parent, + .set_parent = clk_programmable_set_parent, + .set_rate = clk_programmable_set_rate, ++ .save_context = clk_programmable_save_context, ++ .restore_context = clk_programmable_restore_context, + }; + + struct clk_hw * __init +@@ -221,8 +248,6 @@ at91_clk_register_programmable(struct re + if (ret) { + kfree(prog); + hw = ERR_PTR(ret); +- } else { +- pmc_register_pck(id); + } + + return hw; +--- a/drivers/clk/at91/clk-sam9x60-pll.c ++++ b/drivers/clk/at91/clk-sam9x60-pll.c +@@ -38,12 +38,14 @@ struct sam9x60_pll_core { + + struct sam9x60_frac { + struct sam9x60_pll_core core; ++ struct at91_clk_pms pms; + u32 frac; + u16 mul; + }; + + struct sam9x60_div { + struct sam9x60_pll_core core; ++ struct at91_clk_pms pms; + u8 div; + }; + +@@ -75,9 +77,8 @@ static unsigned long sam9x60_frac_pll_re + DIV_ROUND_CLOSEST_ULL((u64)parent_rate * frac->frac, (1 << 22)); + } + +-static int sam9x60_frac_pll_prepare(struct clk_hw *hw) ++static int sam9x60_frac_pll_set(struct sam9x60_pll_core *core) + { +- struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); + struct sam9x60_frac *frac = to_sam9x60_frac(core); + struct regmap *regmap = core->regmap; + unsigned int val, cfrac, cmul; +@@ -141,6 +142,13 @@ unlock: + return 0; + } + ++static int sam9x60_frac_pll_prepare(struct clk_hw *hw) ++{ ++ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); ++ ++ return sam9x60_frac_pll_set(core); ++} ++ + static void sam9x60_frac_pll_unprepare(struct clk_hw *hw) + { + struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); +@@ -280,6 +288,25 @@ unlock: + return ret; + } + ++static int sam9x60_frac_pll_save_context(struct clk_hw *hw) ++{ ++ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); ++ struct sam9x60_frac *frac = to_sam9x60_frac(core); ++ ++ frac->pms.status = sam9x60_pll_ready(core->regmap, core->id); ++ ++ return 0; ++} ++ ++static void sam9x60_frac_pll_restore_context(struct clk_hw *hw) ++{ ++ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); ++ struct sam9x60_frac *frac = to_sam9x60_frac(core); ++ ++ if (frac->pms.status) ++ sam9x60_frac_pll_set(core); ++} ++ + static const struct clk_ops sam9x60_frac_pll_ops = { + .prepare = sam9x60_frac_pll_prepare, + .unprepare = sam9x60_frac_pll_unprepare, +@@ -287,6 +314,8 @@ static const struct clk_ops sam9x60_frac + .recalc_rate = sam9x60_frac_pll_recalc_rate, + .round_rate = sam9x60_frac_pll_round_rate, + .set_rate = sam9x60_frac_pll_set_rate, ++ .save_context = sam9x60_frac_pll_save_context, ++ .restore_context = sam9x60_frac_pll_restore_context, + }; + + static const struct clk_ops sam9x60_frac_pll_ops_chg = { +@@ -296,11 +325,12 @@ static const struct clk_ops sam9x60_frac + .recalc_rate = sam9x60_frac_pll_recalc_rate, + .round_rate = sam9x60_frac_pll_round_rate, + .set_rate = sam9x60_frac_pll_set_rate_chg, ++ .save_context = sam9x60_frac_pll_save_context, ++ .restore_context = sam9x60_frac_pll_restore_context, + }; + +-static int sam9x60_div_pll_prepare(struct clk_hw *hw) ++static int sam9x60_div_pll_set(struct sam9x60_pll_core *core) + { +- struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); + struct sam9x60_div *div = to_sam9x60_div(core); + struct regmap *regmap = core->regmap; + unsigned long flags; +@@ -334,6 +364,13 @@ unlock: + return 0; + } + ++static int sam9x60_div_pll_prepare(struct clk_hw *hw) ++{ ++ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); ++ ++ return sam9x60_div_pll_set(core); ++} ++ + static void sam9x60_div_pll_unprepare(struct clk_hw *hw) + { + struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); +@@ -482,6 +519,25 @@ unlock: + return 0; + } + ++static int sam9x60_div_pll_save_context(struct clk_hw *hw) ++{ ++ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); ++ struct sam9x60_div *div = to_sam9x60_div(core); ++ ++ div->pms.status = sam9x60_div_pll_is_prepared(hw); ++ ++ return 0; ++} ++ ++static void sam9x60_div_pll_restore_context(struct clk_hw *hw) ++{ ++ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); ++ struct sam9x60_div *div = to_sam9x60_div(core); ++ ++ if (div->pms.status) ++ sam9x60_div_pll_set(core); ++} ++ + static const struct clk_ops sam9x60_div_pll_ops = { + .prepare = sam9x60_div_pll_prepare, + .unprepare = sam9x60_div_pll_unprepare, +@@ -489,6 +545,8 @@ static const struct clk_ops sam9x60_div_ + .recalc_rate = sam9x60_div_pll_recalc_rate, + .round_rate = sam9x60_div_pll_round_rate, + .set_rate = sam9x60_div_pll_set_rate, ++ .save_context = sam9x60_div_pll_save_context, ++ .restore_context = sam9x60_div_pll_restore_context, + }; + + static const struct clk_ops sam9x60_div_pll_ops_chg = { +@@ -498,6 +556,8 @@ static const struct clk_ops sam9x60_div_ + .recalc_rate = sam9x60_div_pll_recalc_rate, + .round_rate = sam9x60_div_pll_round_rate, + .set_rate = sam9x60_div_pll_set_rate_chg, ++ .save_context = sam9x60_div_pll_save_context, ++ .restore_context = sam9x60_div_pll_restore_context, + }; + + struct clk_hw * __init +--- a/drivers/clk/at91/clk-system.c ++++ b/drivers/clk/at91/clk-system.c +@@ -20,6 +20,7 @@ + struct clk_system { + struct clk_hw hw; + struct regmap *regmap; ++ struct at91_clk_pms pms; + u8 id; + }; + +@@ -77,10 +78,29 @@ static int clk_system_is_prepared(struct + return !!(status & (1 << sys->id)); + } + ++static int clk_system_save_context(struct clk_hw *hw) ++{ ++ struct clk_system *sys = to_clk_system(hw); ++ ++ sys->pms.status = clk_system_is_prepared(hw); ++ ++ return 0; ++} ++ ++static void clk_system_restore_context(struct clk_hw *hw) ++{ ++ struct clk_system *sys = to_clk_system(hw); ++ ++ if (sys->pms.status) ++ clk_system_prepare(&sys->hw); ++} ++ + static const struct clk_ops system_ops = { + .prepare = clk_system_prepare, + .unprepare = clk_system_unprepare, + .is_prepared = clk_system_is_prepared, ++ .save_context = clk_system_save_context, ++ .restore_context = clk_system_restore_context, + }; + + struct clk_hw * __init +--- a/drivers/clk/at91/clk-usb.c ++++ b/drivers/clk/at91/clk-usb.c +@@ -24,6 +24,7 @@ + struct at91sam9x5_clk_usb { + struct clk_hw hw; + struct regmap *regmap; ++ struct at91_clk_pms pms; + u32 usbs_mask; + u8 num_parents; + }; +@@ -148,12 +149,38 @@ static int at91sam9x5_clk_usb_set_rate(s + return 0; + } + ++static int at91sam9x5_usb_save_context(struct clk_hw *hw) ++{ ++ struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); ++ struct clk_hw *parent_hw = clk_hw_get_parent(hw); ++ ++ usb->pms.parent = at91sam9x5_clk_usb_get_parent(hw); ++ usb->pms.parent_rate = clk_hw_get_rate(parent_hw); ++ usb->pms.rate = at91sam9x5_clk_usb_recalc_rate(hw, usb->pms.parent_rate); ++ ++ return 0; ++} ++ ++static void at91sam9x5_usb_restore_context(struct clk_hw *hw) ++{ ++ struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); ++ int ret; ++ ++ ret = at91sam9x5_clk_usb_set_parent(hw, usb->pms.parent); ++ if (ret) ++ return; ++ ++ at91sam9x5_clk_usb_set_rate(hw, usb->pms.rate, usb->pms.parent_rate); ++} ++ + static const struct clk_ops at91sam9x5_usb_ops = { + .recalc_rate = at91sam9x5_clk_usb_recalc_rate, + .determine_rate = at91sam9x5_clk_usb_determine_rate, + .get_parent = at91sam9x5_clk_usb_get_parent, + .set_parent = at91sam9x5_clk_usb_set_parent, + .set_rate = at91sam9x5_clk_usb_set_rate, ++ .save_context = at91sam9x5_usb_save_context, ++ .restore_context = at91sam9x5_usb_restore_context, + }; + + static int at91sam9n12_clk_usb_enable(struct clk_hw *hw) +--- a/drivers/clk/at91/clk-utmi.c ++++ b/drivers/clk/at91/clk-utmi.c +@@ -23,6 +23,7 @@ struct clk_utmi { + struct clk_hw hw; + struct regmap *regmap_pmc; + struct regmap *regmap_sfr; ++ struct at91_clk_pms pms; + }; + + #define to_clk_utmi(hw) container_of(hw, struct clk_utmi, hw) +@@ -113,11 +114,30 @@ static unsigned long clk_utmi_recalc_rat + return UTMI_RATE; + } + ++static int clk_utmi_save_context(struct clk_hw *hw) ++{ ++ struct clk_utmi *utmi = to_clk_utmi(hw); ++ ++ utmi->pms.status = clk_utmi_is_prepared(hw); ++ ++ return 0; ++} ++ ++static void clk_utmi_restore_context(struct clk_hw *hw) ++{ ++ struct clk_utmi *utmi = to_clk_utmi(hw); ++ ++ if (utmi->pms.status) ++ clk_utmi_prepare(hw); ++} ++ + static const struct clk_ops utmi_ops = { + .prepare = clk_utmi_prepare, + .unprepare = clk_utmi_unprepare, + .is_prepared = clk_utmi_is_prepared, + .recalc_rate = clk_utmi_recalc_rate, ++ .save_context = clk_utmi_save_context, ++ .restore_context = clk_utmi_restore_context, + }; + + static struct clk_hw * __init +@@ -232,10 +252,29 @@ static int clk_utmi_sama7g5_is_prepared( + return 0; + } + ++static int clk_utmi_sama7g5_save_context(struct clk_hw *hw) ++{ ++ struct clk_utmi *utmi = to_clk_utmi(hw); ++ ++ utmi->pms.status = clk_utmi_sama7g5_is_prepared(hw); ++ ++ return 0; ++} ++ ++static void clk_utmi_sama7g5_restore_context(struct clk_hw *hw) ++{ ++ struct clk_utmi *utmi = to_clk_utmi(hw); ++ ++ if (utmi->pms.status) ++ clk_utmi_sama7g5_prepare(hw); ++} ++ + static const struct clk_ops sama7g5_utmi_ops = { + .prepare = clk_utmi_sama7g5_prepare, + .is_prepared = clk_utmi_sama7g5_is_prepared, + .recalc_rate = clk_utmi_recalc_rate, ++ .save_context = clk_utmi_sama7g5_save_context, ++ .restore_context = clk_utmi_sama7g5_restore_context, + }; + + struct clk_hw * __init +--- a/drivers/clk/at91/pmc.c ++++ b/drivers/clk/at91/pmc.c +@@ -3,6 +3,7 @@ + * Copyright (C) 2013 Boris BREZILLON + */ + ++#include + #include + #include + #include +@@ -14,8 +15,6 @@ + + #include + +-#include +- + #include "pmc.h" + + #define PMC_MAX_IDS 128 +@@ -111,147 +110,19 @@ struct pmc_data *pmc_data_allocate(unsig + } + + #ifdef CONFIG_PM +-static struct regmap *pmcreg; +- +-static u8 registered_ids[PMC_MAX_IDS]; +-static u8 registered_pcks[PMC_MAX_PCKS]; +- +-static struct +-{ +- u32 scsr; +- u32 pcsr0; +- u32 uckr; +- u32 mor; +- u32 mcfr; +- u32 pllar; +- u32 mckr; +- u32 usb; +- u32 imr; +- u32 pcsr1; +- u32 pcr[PMC_MAX_IDS]; +- u32 audio_pll0; +- u32 audio_pll1; +- u32 pckr[PMC_MAX_PCKS]; +-} pmc_cache; +- +-/* +- * As Peripheral ID 0 is invalid on AT91 chips, the identifier is stored +- * without alteration in the table, and 0 is for unused clocks. +- */ +-void pmc_register_id(u8 id) ++static int at91_pmc_suspend(void) + { +- int i; +- +- for (i = 0; i < PMC_MAX_IDS; i++) { +- if (registered_ids[i] == 0) { +- registered_ids[i] = id; +- break; +- } +- if (registered_ids[i] == id) +- break; +- } ++ return clk_save_context(); + } + +-/* +- * As Programmable Clock 0 is valid on AT91 chips, there is an offset +- * of 1 between the stored value and the real clock ID. +- */ +-void pmc_register_pck(u8 pck) ++static void at91_pmc_resume(void) + { +- int i; +- +- for (i = 0; i < PMC_MAX_PCKS; i++) { +- if (registered_pcks[i] == 0) { +- registered_pcks[i] = pck + 1; +- break; +- } +- if (registered_pcks[i] == (pck + 1)) +- break; +- } +-} +- +-static int pmc_suspend(void) +-{ +- int i; +- u8 num; +- +- regmap_read(pmcreg, AT91_PMC_SCSR, &pmc_cache.scsr); +- regmap_read(pmcreg, AT91_PMC_PCSR, &pmc_cache.pcsr0); +- regmap_read(pmcreg, AT91_CKGR_UCKR, &pmc_cache.uckr); +- regmap_read(pmcreg, AT91_CKGR_MOR, &pmc_cache.mor); +- regmap_read(pmcreg, AT91_CKGR_MCFR, &pmc_cache.mcfr); +- regmap_read(pmcreg, AT91_CKGR_PLLAR, &pmc_cache.pllar); +- regmap_read(pmcreg, AT91_PMC_MCKR, &pmc_cache.mckr); +- regmap_read(pmcreg, AT91_PMC_USB, &pmc_cache.usb); +- regmap_read(pmcreg, AT91_PMC_IMR, &pmc_cache.imr); +- regmap_read(pmcreg, AT91_PMC_PCSR1, &pmc_cache.pcsr1); +- +- for (i = 0; registered_ids[i]; i++) { +- regmap_write(pmcreg, AT91_PMC_PCR, +- (registered_ids[i] & AT91_PMC_PCR_PID_MASK)); +- regmap_read(pmcreg, AT91_PMC_PCR, +- &pmc_cache.pcr[registered_ids[i]]); +- } +- for (i = 0; registered_pcks[i]; i++) { +- num = registered_pcks[i] - 1; +- regmap_read(pmcreg, AT91_PMC_PCKR(num), &pmc_cache.pckr[num]); +- } +- +- return 0; +-} +- +-static bool pmc_ready(unsigned int mask) +-{ +- unsigned int status; +- +- regmap_read(pmcreg, AT91_PMC_SR, &status); +- +- return ((status & mask) == mask) ? 1 : 0; +-} +- +-static void pmc_resume(void) +-{ +- int i; +- u8 num; +- u32 tmp; +- u32 mask = AT91_PMC_MCKRDY | AT91_PMC_LOCKA; +- +- regmap_read(pmcreg, AT91_PMC_MCKR, &tmp); +- if (pmc_cache.mckr != tmp) +- pr_warn("MCKR was not configured properly by the firmware\n"); +- regmap_read(pmcreg, AT91_CKGR_PLLAR, &tmp); +- if (pmc_cache.pllar != tmp) +- pr_warn("PLLAR was not configured properly by the firmware\n"); +- +- regmap_write(pmcreg, AT91_PMC_SCER, pmc_cache.scsr); +- regmap_write(pmcreg, AT91_PMC_PCER, pmc_cache.pcsr0); +- regmap_write(pmcreg, AT91_CKGR_UCKR, pmc_cache.uckr); +- regmap_write(pmcreg, AT91_CKGR_MOR, pmc_cache.mor); +- regmap_write(pmcreg, AT91_CKGR_MCFR, pmc_cache.mcfr); +- regmap_write(pmcreg, AT91_PMC_USB, pmc_cache.usb); +- regmap_write(pmcreg, AT91_PMC_IMR, pmc_cache.imr); +- regmap_write(pmcreg, AT91_PMC_PCER1, pmc_cache.pcsr1); +- +- for (i = 0; registered_ids[i]; i++) { +- regmap_write(pmcreg, AT91_PMC_PCR, +- pmc_cache.pcr[registered_ids[i]] | +- AT91_PMC_PCR_CMD); +- } +- for (i = 0; registered_pcks[i]; i++) { +- num = registered_pcks[i] - 1; +- regmap_write(pmcreg, AT91_PMC_PCKR(num), pmc_cache.pckr[num]); +- } +- +- if (pmc_cache.uckr & AT91_PMC_UPLLEN) +- mask |= AT91_PMC_LOCKU; +- +- while (!pmc_ready(mask)) +- cpu_relax(); ++ clk_restore_context(); + } + + static struct syscore_ops pmc_syscore_ops = { +- .suspend = pmc_suspend, +- .resume = pmc_resume, ++ .suspend = at91_pmc_suspend, ++ .resume = at91_pmc_resume, + }; + + static const struct of_device_id sama5d2_pmc_dt_ids[] = { +@@ -271,11 +142,7 @@ static int __init pmc_register_ops(void) + of_node_put(np); + return -ENODEV; + } +- +- pmcreg = device_node_to_regmap(np); + of_node_put(np); +- if (IS_ERR(pmcreg)) +- return PTR_ERR(pmcreg); + + register_syscore_ops(&pmc_syscore_ops); + +--- a/drivers/clk/at91/pmc.h ++++ b/drivers/clk/at91/pmc.h +@@ -13,6 +13,8 @@ + #include + #include + ++#include ++ + extern spinlock_t pmc_pcr_lock; + + struct pmc_data { +@@ -98,6 +100,20 @@ struct clk_pcr_layout { + u32 pid_mask; + }; + ++/** ++ * struct at91_clk_pms - Power management state for AT91 clock ++ * @rate: clock rate ++ * @parent_rate: clock parent rate ++ * @status: clock status (enabled or disabled) ++ * @parent: clock parent index ++ */ ++struct at91_clk_pms { ++ unsigned long rate; ++ unsigned long parent_rate; ++ unsigned int status; ++ unsigned int parent; ++}; ++ + #define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1)) + #define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask)) + +@@ -248,12 +264,4 @@ struct clk_hw * __init + at91_clk_sama7g5_register_utmi(struct regmap *regmap, const char *name, + const char *parent_name); + +-#ifdef CONFIG_PM +-void pmc_register_id(u8 id); +-void pmc_register_pck(u8 pck); +-#else +-static inline void pmc_register_id(u8 id) {} +-static inline void pmc_register_pck(u8 pck) {} +-#endif +- + #endif /* __PMC_H_ */