linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RESEND PATCH 0/5] clk: at91: updates for power management and dvfs
@ 2021-03-24  9:43 Claudiu Beznea
  2021-03-24  9:43 ` [RESEND PATCH 1/5] clk: at91: re-factor clocks suspend/resume Claudiu Beznea
                   ` (4 more replies)
  0 siblings, 5 replies; 11+ messages in thread
From: Claudiu Beznea @ 2021-03-24  9:43 UTC (permalink / raw)
  To: mturquette, sboyd, nicolas.ferre, alexandre.belloni, ludovic.desroches
  Cc: linux-clk, linux-arm-kernel, linux-kernel, Claudiu Beznea

Hi,

This series addresses the power management for SAMA7G5 and also updates
the master clock prescaller and master clock divider drivers to accommodate
the requests at [1].

The power management part is implemented by adding
save_context()/restore_context() on each clock driver (patch 1/5). Since
the PM part is necessary only for backup mode (supported on SAMA5D2 and
SAMA7G5) the pmc.c has been adapted to call the
save_context()/restore_context() only on switches to/from backup mode
(patch 2/5).

Patch 3/5 adds the securam clock on SAMA7G5. This is necessary for
backup mode of SAMA7G5.

Patch 4/5 adds registers definitions for the new master clocks (MCK1..4) of
SAMA7G5 and adapt the clk-master.c. The defines exported in
include/linux/clk/at91_pmc.h will be also used by platform specific PM
code.

Patch 5/5 adpat the master clock divider and master clock prescaller
to address the requests at [1].

Thank you,
Claudiu Beznea

[1] https://lore.kernel.org/lkml/20210105104426.4tmgc2l3vyicwedd@vireshk-i7/

Claudiu Beznea (5):
  clk: at91: re-factor clocks suspend/resume
  clk: at91: pmc: execute suspend/resume only for backup mode
  clk: at91: sama7g5: add securam's peripheral clock
  clk: at91: clk-master: add register definition for sama7g5's master
    clock
  clk: at91: clk-master: update for dvfs

 drivers/clk/at91/at91rm9200.c       |   2 +-
 drivers/clk/at91/at91sam9260.c      |   2 +-
 drivers/clk/at91/at91sam9g45.c      |   2 +-
 drivers/clk/at91/at91sam9n12.c      |   2 +-
 drivers/clk/at91/at91sam9rl.c       |   2 +-
 drivers/clk/at91/at91sam9x5.c       |   2 +-
 drivers/clk/at91/clk-generated.c    |  45 +++-
 drivers/clk/at91/clk-main.c         |  66 +++++
 drivers/clk/at91/clk-master.c       | 395 +++++++++++++++++++++-------
 drivers/clk/at91/clk-peripheral.c   |  38 ++-
 drivers/clk/at91/clk-pll.c          |  37 +++
 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         |  47 +++-
 drivers/clk/at91/dt-compat.c        |   2 +-
 drivers/clk/at91/pmc.c              | 175 +++---------
 drivers/clk/at91/pmc.h              |  27 +-
 drivers/clk/at91/sam9x60.c          |   2 +-
 drivers/clk/at91/sama5d2.c          |   2 +-
 drivers/clk/at91/sama5d3.c          |   2 +-
 drivers/clk/at91/sama5d4.c          |   2 +-
 drivers/clk/at91/sama7g5.c          |   3 +-
 include/linux/clk/at91_pmc.h        |  26 ++
 25 files changed, 750 insertions(+), 275 deletions(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [RESEND PATCH 1/5] clk: at91: re-factor clocks suspend/resume
  2021-03-24  9:43 [RESEND PATCH 0/5] clk: at91: updates for power management and dvfs Claudiu Beznea
@ 2021-03-24  9:43 ` Claudiu Beznea
  2021-03-30 17:14   ` Nicolas Ferre
  2021-03-24  9:43 ` [RESEND PATCH 2/5] clk: at91: pmc: execute suspend/resume only for backup mode Claudiu Beznea
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 11+ messages in thread
From: Claudiu Beznea @ 2021-03-24  9:43 UTC (permalink / raw)
  To: mturquette, sboyd, nicolas.ferre, alexandre.belloni, ludovic.desroches
  Cc: linux-clk, linux-arm-kernel, linux-kernel, Claudiu Beznea

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 with SAMA5D2's PMC
in mind. SAMA7G's 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 <claudiu.beznea@microchip.com>
---
 drivers/clk/at91/clk-generated.c    |  45 +++++--
 drivers/clk/at91/clk-main.c         |  66 ++++++++++
 drivers/clk/at91/clk-master.c       | 183 ++++++++++++++++++++++++++--
 drivers/clk/at91/clk-peripheral.c   |  38 +++++-
 drivers/clk/at91/clk-pll.c          |  37 ++++++
 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         |  47 ++++++-
 drivers/clk/at91/pmc.c              | 149 ++--------------------
 drivers/clk/at91/pmc.h              |  24 ++--
 12 files changed, 554 insertions(+), 179 deletions(-)

diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
index b4fc8d71daf2..0e436f9e7508 100644
--- 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,34 @@ 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);
-
 	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 | (status ? AT91_PMC_PCR_GCKEN : 0),
 			   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);
+			   (status ? AT91_PMC_PCR_GCKEN : 0));
 	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;
 }
 
@@ -239,6 +249,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,
@@ -248,6 +275,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,
 };
 
 /**
@@ -314,8 +343,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;
diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c
index cfae2f59df66..8601b27c1ae0 100644
--- 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(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
@@ -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
@@ -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
diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c
index a80427980bf7..6f1fb2eb2a8d 100644
--- 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_recalc_rate(struct clk_hw *hw,
 	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(struct clk_hw *hw, unsigned long rate,
 	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(struct clk_hw *hw, unsigned long rate,
 		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(struct clk_hw *hw,
 	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(struct clk_hw *hw, unsigned long rate,
 {
 	struct clk_master *master = to_clk_master(hw);
 	unsigned long flags;
-	unsigned int pres;
+	unsigned int pres, val;
+	int ret;
 
 	pres = DIV_ROUND_CLOSEST(parent_rate, rate);
 	if (pres > MASTER_PRES_MAX)
@@ -284,15 +356,24 @@ static int clk_master_pres_set_rate(struct clk_hw *hw, unsigned long rate,
 		pres = ffs(pres) - 1;
 
 	spin_lock_irqsave(master->lock, flags);
+	ret = regmap_read(master->regmap, master->layout->offset, &val);
+	if (ret)
+		goto unlock;
+
+	val = (val >> master->layout->pres_shift) & MASTER_PRES_MASK;
+	if (pres == val)
+		goto unlock;
+
 	regmap_update_bits(master->regmap, master->layout->offset,
 			   (MASTER_PRES_MASK << master->layout->pres_shift),
 			   (pres << master->layout->pres_shift));
 
 	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 +411,66 @@ static u8 clk_master_pres_get_parent(struct clk_hw *hw)
 	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);
+
+	pres = (val >> master->layout->pres_shift) & MASTER_PRES_MASK;
+	if (pres == 3 && 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);
+
+	pres = (val >> master->layout->pres_shift) & MASTER_PRES_MASK;
+	if (pres == 3 && 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 +480,8 @@ static const struct clk_ops master_pres_ops_chg = {
 	.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,9 +677,9 @@ static int clk_sama7g5_master_set_parent(struct clk_hw *hw, u8 index)
 	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;
 
@@ -550,9 +688,10 @@ static int clk_sama7g5_master_enable(struct clk_hw *hw)
 	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 |
+			   (status ? PMC_MCR_EN : 0) | PMC_MCR_CSS | PMC_MCR_DIV |
 			   PMC_MCR_CMD | PMC_MCR_ID_MSK,
-			   PMC_MCR_EN | (master->parent << PMC_MCR_CSS_SHIFT) |
+			   (status ? PMC_MCR_EN : 0) |
+			   (master->parent << PMC_MCR_CSS_SHIFT) |
 			   (master->div << MASTER_DIV_SHIFT) |
 			   PMC_MCR_CMD | PMC_MCR_ID(master->id));
 
@@ -563,6 +702,13 @@ static int clk_sama7g5_master_enable(struct clk_hw *hw)
 		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 +766,23 @@ static int clk_sama7g5_master_set_rate(struct clk_hw *hw, unsigned long rate,
 	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 +792,8 @@ static const struct clk_ops sama7g5_master_ops = {
 	.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
diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c
index 7a27ba8e0577..bf088cd06225 100644
--- 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,9 +156,9 @@ static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph)
 	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;
 
 	if (periph->id < PERIPHERAL_ID_MIN)
@@ -168,15 +169,22 @@ static int clk_sam9x5_peripheral_enable(struct clk_hw *hw)
 		     (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,
+			   (status ? AT91_PMC_PCR_EN : 0),
 			   field_prep(periph->layout->div_mask, periph->div) |
 			   periph->layout->cmd |
-			   AT91_PMC_PCR_EN);
+			   (status ? AT91_PMC_PCR_EN : 0));
 	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_rate(struct clk_hw *hw,
 	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_peripheral_ops = {
 	.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_peripheral_chg_ops = {
 	.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(struct regmap *regmap, spinlock_t *lock,
 		hw = ERR_PTR(ret);
 	} else {
 		clk_sam9x5_peripheral_autodiv(periph);
-		pmc_register_id(id);
 	}
 
 	return hw;
diff --git a/drivers/clk/at91/clk-pll.c b/drivers/clk/at91/clk-pll.c
index 6ed986d3eee0..31964622c658 100644
--- 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,40 @@ static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
 	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;
+	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);
+
+	if (pll->pms.rate != calc_rate ||
+	    pll->pms.status != clk_pll_ready(pll->regmap, PLL_REG(pll->id)) ||
+	    (((pllr >> PLL_COUNT_SHIFT) & PLL_MAX_COUNT) != PLL_MAX_COUNT) ||
+	    (out && ((pllr >> PLL_OUT_SHIFT) & 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 +302,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
diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c
index fcf8f6a1c2c6..6c4b259d31d3 100644
--- 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(struct clk_hw *hw, unsigned long rate,
 	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 regmap *regmap,
 	if (ret) {
 		kfree(prog);
 		hw = ERR_PTR(ret);
-	} else {
-		pmc_register_pck(id);
 	}
 
 	return hw;
diff --git a/drivers/clk/at91/clk-sam9x60-pll.c b/drivers/clk/at91/clk-sam9x60-pll.c
index 34e3ab13741a..7020d3bf6e13 100644
--- 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_recalc_rate(struct clk_hw *hw,
 		((u64)parent_rate * frac->frac >> 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 @@ static int sam9x60_frac_pll_prepare(struct clk_hw *hw)
 	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 @@ static int sam9x60_frac_pll_set_rate_chg(struct clk_hw *hw, unsigned long rate,
 	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_pll_ops = {
 	.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_pll_ops_chg = {
 	.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 @@ static int sam9x60_div_pll_prepare(struct clk_hw *hw)
 	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 @@ static int sam9x60_div_pll_set_rate_chg(struct clk_hw *hw, unsigned long rate,
 	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_pll_ops = {
 	.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_pll_ops_chg = {
 	.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
diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c
index f83ec0de86c3..80720fd1a9cf 100644
--- 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 clk_hw *hw)
 	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
diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c
index 31d5c45e30d7..b0696a928aa9 100644
--- 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(struct clk_hw *hw, unsigned long rate,
 	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)
diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c
index df9f3fc3b6a6..2b102ee2a7d3 100644
--- 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_rate(struct clk_hw *hw,
 	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,35 @@ static int clk_utmi_sama7g5_is_prepared(struct clk_hw *hw)
 	return 0;
 }
 
+static unsigned long clk_utmi_sama7g5_recalc_rate(struct clk_hw *hw,
+						  unsigned long parent_rate)
+{
+	return 480000000;
+}
+
+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,
+	.recalc_rate = clk_utmi_sama7g5_recalc_rate,
+	.save_context = clk_utmi_sama7g5_save_context,
+	.restore_context = clk_utmi_sama7g5_restore_context,
 };
 
 struct clk_hw * __init
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 20ee9dccee78..77b57c9f5dcb 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -3,6 +3,7 @@
  *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
  */
 
+#include <linux/clk.h>
 #include <linux/clk-provider.h>
 #include <linux/clkdev.h>
 #include <linux/clk/at91_pmc.h>
@@ -14,8 +15,6 @@
 
 #include <asm/proc-fns.h>
 
-#include <dt-bindings/clock/at91.h>
-
 #include "pmc.h"
 
 #define PMC_MAX_IDS 128
@@ -111,147 +110,19 @@ struct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem,
 }
 
 #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)
-{
-	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;
-	}
-}
-
-/*
- * 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)
-{
-	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)
+static int at91_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;
+	return clk_save_context();
 }
 
-static bool pmc_ready(unsigned int mask)
+static void at91_pmc_resume(void)
 {
-	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[] = {
@@ -265,12 +136,8 @@ static int __init pmc_register_ops(void)
 
 	np = of_find_matching_node(NULL, sama5d2_pmc_dt_ids);
 	if (!np)
-		return -ENODEV;
-
-	pmcreg = device_node_to_regmap(np);
+		return 0;
 	of_node_put(np);
-	if (IS_ERR(pmcreg))
-		return PTR_ERR(pmcreg);
 
 	register_syscore_ops(&pmc_syscore_ops);
 
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index a49076c804a9..86580ebd9ad9 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -13,6 +13,8 @@
 #include <linux/regmap.h>
 #include <linux/spinlock.h>
 
+#include <dt-bindings/clock/at91.h>
+
 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
+ * @status: clock status (enabled or disabled)
+ * @parent: clock parent index
+ * @parent_rate: clock parent rate
+ * @rate: clock rate
+ */
+struct at91_clk_pms {
+	unsigned int status;
+	unsigned int parent;
+	unsigned long parent_rate;
+	unsigned long rate;
+};
+
 #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_ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RESEND PATCH 2/5] clk: at91: pmc: execute suspend/resume only for backup mode
  2021-03-24  9:43 [RESEND PATCH 0/5] clk: at91: updates for power management and dvfs Claudiu Beznea
  2021-03-24  9:43 ` [RESEND PATCH 1/5] clk: at91: re-factor clocks suspend/resume Claudiu Beznea
@ 2021-03-24  9:43 ` Claudiu Beznea
  2021-03-24  9:43 ` [RESEND PATCH 3/5] clk: at91: sama7g5: add securam's peripheral clock Claudiu Beznea
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 11+ messages in thread
From: Claudiu Beznea @ 2021-03-24  9:43 UTC (permalink / raw)
  To: mturquette, sboyd, nicolas.ferre, alexandre.belloni, ludovic.desroches
  Cc: linux-clk, linux-arm-kernel, linux-kernel, Claudiu Beznea

Before going to backup mode architecture specific PM code sets the first
word in securam (and it will be cleared in a subsequent commit for the rest
of power saving modes). Thus take this into account when
suspending/resuming clocks. This will avoid executing unnecessary
instructions when suspending to non backup modes. Since the clear of the
1st word in securam will be done in a subsequent commit the current commit
will not broke the current behavior since up to this moment the
suspend/resume were executed for all AT91 PM modes. The difference now
is that the suspend/resume for clocks will be executed for the rest of
AT91 PM modes just after the 1st enter/exit to/from backup mode.
Also this commit changed the postcore_initcall() with subsys_initcall()
to be able to execute of_find_compatible_node() since this was not
available at the moment of postcore_initcall(). This should not alter
the tcb_clksrc since the changes are related to clocks suspend/resume
procedure that will be executed at the user space request, thus long
ago after postcore_initcall().

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/clk/at91/pmc.c | 48 ++++++++++++++++++++++++++++++++++--------
 1 file changed, 39 insertions(+), 9 deletions(-)

diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 77b57c9f5dcb..c226d33cd899 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -8,6 +8,8 @@
 #include <linux/clkdev.h>
 #include <linux/clk/at91_pmc.h>
 #include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
 #include <linux/mfd/syscon.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
@@ -110,13 +112,35 @@ struct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem,
 }
 
 #ifdef CONFIG_PM
+
+/* Address in SECURAM that say if we suspend to backup mode. */
+static void __iomem *at91_pmc_backup_suspend;
+
 static int at91_pmc_suspend(void)
 {
+	unsigned int backup;
+
+	if (!at91_pmc_backup_suspend)
+		return 0;
+
+	backup = *(unsigned int *)at91_pmc_backup_suspend;
+	if (!backup)
+		return 0;
+
 	return clk_save_context();
 }
 
 static void at91_pmc_resume(void)
 {
+	unsigned int backup;
+
+	if (!at91_pmc_backup_suspend)
+		return;
+
+	backup = *(unsigned int *)at91_pmc_backup_suspend;
+	if (!backup)
+		return;
+
 	clk_restore_context();
 }
 
@@ -125,24 +149,30 @@ static struct syscore_ops pmc_syscore_ops = {
 	.resume = at91_pmc_resume,
 };
 
-static const struct of_device_id sama5d2_pmc_dt_ids[] = {
-	{ .compatible = "atmel,sama5d2-pmc" },
-	{ /* sentinel */ }
-};
-
 static int __init pmc_register_ops(void)
 {
+	struct platform_device *pdev;
 	struct device_node *np;
 
-	np = of_find_matching_node(NULL, sama5d2_pmc_dt_ids);
+	np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-securam");
 	if (!np)
-		return 0;
+		return -ENODEV;
+
+	pdev = of_find_device_by_node(np);
 	of_node_put(np);
+	if (!pdev)
+		return -ENODEV;
+
+	at91_pmc_backup_suspend = of_iomap(np, 0);
+	if (!at91_pmc_backup_suspend) {
+		pr_warn("%s(): unable to map securam\n", __func__);
+		return -ENOMEM;
+	}
 
 	register_syscore_ops(&pmc_syscore_ops);
 
 	return 0;
 }
-/* This has to happen before arch_initcall because of the tcb_clksrc driver */
-postcore_initcall(pmc_register_ops);
+
+subsys_initcall(pmc_register_ops);
 #endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RESEND PATCH 3/5] clk: at91: sama7g5: add securam's peripheral clock
  2021-03-24  9:43 [RESEND PATCH 0/5] clk: at91: updates for power management and dvfs Claudiu Beznea
  2021-03-24  9:43 ` [RESEND PATCH 1/5] clk: at91: re-factor clocks suspend/resume Claudiu Beznea
  2021-03-24  9:43 ` [RESEND PATCH 2/5] clk: at91: pmc: execute suspend/resume only for backup mode Claudiu Beznea
@ 2021-03-24  9:43 ` Claudiu Beznea
  2021-03-30 17:27   ` Nicolas Ferre
  2021-03-24  9:43 ` [RESEND PATCH 4/5] clk: at91: clk-master: add register definition for sama7g5's master clock Claudiu Beznea
  2021-03-24  9:43 ` [RESEND PATCH 5/5] clk: at91: clk-master: update for dvfs Claudiu Beznea
  4 siblings, 1 reply; 11+ messages in thread
From: Claudiu Beznea @ 2021-03-24  9:43 UTC (permalink / raw)
  To: mturquette, sboyd, nicolas.ferre, alexandre.belloni, ludovic.desroches
  Cc: linux-clk, linux-arm-kernel, linux-kernel, Claudiu Beznea

Add SECURAM's peripheral clock.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/clk/at91/sama7g5.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/clk/at91/sama7g5.c b/drivers/clk/at91/sama7g5.c
index a6e20b35960e..28e26fb90417 100644
--- a/drivers/clk/at91/sama7g5.c
+++ b/drivers/clk/at91/sama7g5.c
@@ -377,6 +377,7 @@ static const struct {
 	u8 id;
 } sama7g5_periphck[] = {
 	{ .n = "pioA_clk",	.p = "mck0", .id = 11, },
+	{ .n = "securam_clk",	.p = "mck0", .id = 18, },
 	{ .n = "sfr_clk",	.p = "mck1", .id = 19, },
 	{ .n = "hsmc_clk",	.p = "mck1", .id = 21, },
 	{ .n = "xdmac0_clk",	.p = "mck1", .id = 22, },
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RESEND PATCH 4/5] clk: at91: clk-master: add register definition for sama7g5's master clock
  2021-03-24  9:43 [RESEND PATCH 0/5] clk: at91: updates for power management and dvfs Claudiu Beznea
                   ` (2 preceding siblings ...)
  2021-03-24  9:43 ` [RESEND PATCH 3/5] clk: at91: sama7g5: add securam's peripheral clock Claudiu Beznea
@ 2021-03-24  9:43 ` Claudiu Beznea
  2021-03-30 17:23   ` Nicolas Ferre
  2021-03-24  9:43 ` [RESEND PATCH 5/5] clk: at91: clk-master: update for dvfs Claudiu Beznea
  4 siblings, 1 reply; 11+ messages in thread
From: Claudiu Beznea @ 2021-03-24  9:43 UTC (permalink / raw)
  To: mturquette, sboyd, nicolas.ferre, alexandre.belloni, ludovic.desroches
  Cc: linux-clk, linux-arm-kernel, linux-kernel, Claudiu Beznea

Add register definitions for SAMA7G5's master clock. These would be
also used by architecture specific power saving code. With this, update
also clk-master.c.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/clk/at91/clk-master.c | 51 +++++++++++++++++------------------
 include/linux/clk/at91_pmc.h  | 26 ++++++++++++++++++
 2 files changed, 50 insertions(+), 27 deletions(-)

diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c
index 6f1fb2eb2a8d..a6a393bb1def 100644
--- a/drivers/clk/at91/clk-master.c
+++ b/drivers/clk/at91/clk-master.c
@@ -17,15 +17,7 @@
 #define MASTER_DIV_SHIFT	8
 #define MASTER_DIV_MASK		0x7
 
-#define PMC_MCR			0x30
-#define PMC_MCR_ID_MSK		GENMASK(3, 0)
-#define PMC_MCR_CMD		BIT(7)
-#define PMC_MCR_DIV		GENMASK(10, 8)
-#define PMC_MCR_CSS		GENMASK(20, 16)
 #define PMC_MCR_CSS_SHIFT	(16)
-#define PMC_MCR_EN		BIT(28)
-
-#define PMC_MCR_ID(x)		((x) & PMC_MCR_ID_MSK)
 
 #define MASTER_MAX_ID		4
 
@@ -685,17 +677,20 @@ static void clk_sama7g5_master_set(struct clk_master *master,
 
 	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,
-			   (status ? PMC_MCR_EN : 0) | PMC_MCR_CSS | PMC_MCR_DIV |
-			   PMC_MCR_CMD | PMC_MCR_ID_MSK,
-			   (status ? PMC_MCR_EN : 0) |
+	regmap_write(master->regmap, AT91_PMC_MCR_V2,
+		     AT91_PMC_MCR_V2_ID(master->id));
+	regmap_read(master->regmap, AT91_PMC_MCR_V2, &val);
+	regmap_update_bits(master->regmap, AT91_PMC_MCR_V2,
+			   (status ? AT91_PMC_MCR_V2_EN : 0) |
+			   AT91_PMC_MCR_V2_CSS | AT91_PMC_MCR_V2_DIV |
+			   AT91_PMC_MCR_V2_CMD | AT91_PMC_MCR_V2_ID_MSK,
+			   (status ? AT91_PMC_MCR_V2_EN : 0) |
 			   (master->parent << PMC_MCR_CSS_SHIFT) |
 			   (master->div << MASTER_DIV_SHIFT) |
-			   PMC_MCR_CMD | PMC_MCR_ID(master->id));
+			   AT91_PMC_MCR_V2_CMD |
+			   AT91_PMC_MCR_V2_ID(master->id));
 
-	cparent = (val & PMC_MCR_CSS) >> PMC_MCR_CSS_SHIFT;
+	cparent = (val & AT91_PMC_MCR_V2_CSS) >> PMC_MCR_CSS_SHIFT;
 
 	/* Wait here only if parent is being changed. */
 	while ((cparent != master->parent) && !clk_master_ready(master))
@@ -720,10 +715,12 @@ static void clk_sama7g5_master_disable(struct clk_hw *hw)
 
 	spin_lock_irqsave(master->lock, flags);
 
-	regmap_write(master->regmap, PMC_MCR, master->id);
-	regmap_update_bits(master->regmap, PMC_MCR,
-			   PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK,
-			   PMC_MCR_CMD | PMC_MCR_ID(master->id));
+	regmap_write(master->regmap, AT91_PMC_MCR_V2, master->id);
+	regmap_update_bits(master->regmap, AT91_PMC_MCR_V2,
+			   AT91_PMC_MCR_V2_EN | AT91_PMC_MCR_V2_CMD |
+			   AT91_PMC_MCR_V2_ID_MSK,
+			   AT91_PMC_MCR_V2_CMD |
+			   AT91_PMC_MCR_V2_ID(master->id));
 
 	spin_unlock_irqrestore(master->lock, flags);
 }
@@ -736,12 +733,12 @@ static int clk_sama7g5_master_is_enabled(struct clk_hw *hw)
 
 	spin_lock_irqsave(master->lock, flags);
 
-	regmap_write(master->regmap, PMC_MCR, master->id);
-	regmap_read(master->regmap, PMC_MCR, &val);
+	regmap_write(master->regmap, AT91_PMC_MCR_V2, master->id);
+	regmap_read(master->regmap, AT91_PMC_MCR_V2, &val);
 
 	spin_unlock_irqrestore(master->lock, flags);
 
-	return !!(val & PMC_MCR_EN);
+	return !!(val & AT91_PMC_MCR_V2_EN);
 }
 
 static int clk_sama7g5_master_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -837,10 +834,10 @@ at91_clk_sama7g5_register_master(struct regmap *regmap,
 	master->mux_table = mux_table;
 
 	spin_lock_irqsave(master->lock, flags);
-	regmap_write(master->regmap, PMC_MCR, master->id);
-	regmap_read(master->regmap, PMC_MCR, &val);
-	master->parent = (val & PMC_MCR_CSS) >> PMC_MCR_CSS_SHIFT;
-	master->div = (val & PMC_MCR_DIV) >> MASTER_DIV_SHIFT;
+	regmap_write(master->regmap, AT91_PMC_MCR_V2, master->id);
+	regmap_read(master->regmap, AT91_PMC_MCR_V2, &val);
+	master->parent = (val & AT91_PMC_MCR_V2_CSS) >> PMC_MCR_CSS_SHIFT;
+	master->div = (val & AT91_PMC_MCR_V2_DIV) >> MASTER_DIV_SHIFT;
 	spin_unlock_irqrestore(master->lock, flags);
 
 	hw = &master->hw;
diff --git a/include/linux/clk/at91_pmc.h b/include/linux/clk/at91_pmc.h
index a4f82e836a7c..ccb3f034bfa9 100644
--- a/include/linux/clk/at91_pmc.h
+++ b/include/linux/clk/at91_pmc.h
@@ -137,6 +137,32 @@
 #define			AT91_PMC_PLLADIV2_ON		(1 << 12)
 #define		AT91_PMC_H32MXDIV	BIT(24)
 
+#define	AT91_PMC_MCR_V2		0x30				/* Master Clock Register [SAMA7G5 only] */
+#define		AT91_PMC_MCR_V2_ID_MSK	(0xF)
+#define			AT91_PMC_MCR_V2_ID(_id)		((_id) & AT91_PMC_MCR_V2_ID_MSK)
+#define		AT91_PMC_MCR_V2_CMD	(1 << 7)
+#define		AT91_PMC_MCR_V2_DIV	(7 << 8)
+#define			AT91_PMC_MCR_V2_DIV1		(0 << 8)
+#define			AT91_PMC_MCR_V2_DIV2		(1 << 8)
+#define			AT91_PMC_MCR_V2_DIV4		(2 << 8)
+#define			AT91_PMC_MCR_V2_DIV8		(3 << 8)
+#define			AT91_PMC_MCR_V2_DIV16		(4 << 8)
+#define			AT91_PMC_MCR_V2_DIV32		(5 << 8)
+#define			AT91_PMC_MCR_V2_DIV64		(6 << 8)
+#define			AT91_PMC_MCR_V2_DIV3		(7 << 8)
+#define		AT91_PMC_MCR_V2_CSS	(0x1F << 16)
+#define			AT91_PMC_MCR_V2_CSS_MD_SLCK	(0 << 16)
+#define			AT91_PMC_MCR_V2_CSS_TD_SLCK	(1 << 16)
+#define			AT91_PMC_MCR_V2_CSS_MAINCK	(2 << 16)
+#define			AT91_PMC_MCR_V2_CSS_MCK0	(3 << 16)
+#define			AT91_PMC_MCR_V2_CSS_SYSPLL	(5 << 16)
+#define			AT91_PMC_MCR_V2_CSS_DDRPLL	(6 << 16)
+#define			AT91_PMC_MCR_V2_CSS_IMGPLL	(7 << 16)
+#define			AT91_PMC_MCR_V2_CSS_BAUDPLL	(8 << 16)
+#define			AT91_PMC_MCR_V2_CSS_AUDIOPLL	(9 << 16)
+#define			AT91_PMC_MCR_V2_CSS_ETHPLL	(10 << 16)
+#define		AT91_PMC_MCR_V2_EN	(1 << 28)
+
 #define AT91_PMC_XTALF		0x34			/* Main XTAL Frequency Register [SAMA7G5 only] */
 
 #define	AT91_PMC_USB		0x38			/* USB Clock Register [some SAM9 only] */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RESEND PATCH 5/5] clk: at91: clk-master: update for dvfs
  2021-03-24  9:43 [RESEND PATCH 0/5] clk: at91: updates for power management and dvfs Claudiu Beznea
                   ` (3 preceding siblings ...)
  2021-03-24  9:43 ` [RESEND PATCH 4/5] clk: at91: clk-master: add register definition for sama7g5's master clock Claudiu Beznea
@ 2021-03-24  9:43 ` Claudiu Beznea
  4 siblings, 0 replies; 11+ messages in thread
From: Claudiu Beznea @ 2021-03-24  9:43 UTC (permalink / raw)
  To: mturquette, sboyd, nicolas.ferre, alexandre.belloni, ludovic.desroches
  Cc: linux-clk, linux-arm-kernel, linux-kernel, Claudiu Beznea

SAMA7G5 supports CPU DVFS. The hardware block diagram for the clock
system generating CPU clock is as follows:

                               +--------+
                           +-->|divider1|--> CPU clock
                           |   +--------+
+--------+   +----------+  |   +--------+
|CPU PLL |-->|prescaller|--+-->|divider0|--> MCK0 clock
+--------+   +----------+      +--------+

The divider1 is not implemented in software since the supported CPU
frequencies could be directly retrieved from CPU PLL + prescaller.
Every time the CPU clock needs to be changed the MCK0 should also be
changed to avoid its over/under clocking and also to preserve its
initial value (200MHz). MCK0 feeds IPs that are glitch free aware.

The initial approach for implementing DVFS was to implement MCK0 and
CPU clocks as 2 different Linux clocks, to pass these two clocks to CPUFreq
driver and in CPUFreq driver to run clk_set_rate() on CPU and MCK clocks
accordingly. E.g. if 1GHz speed was requested by CPUFreq subsystem the
following were executed in CPUFreq driver:

/* ... */
clk_set_rate(mck0, intermediary_freq_to_avoid_over_under_clocking);
clk_set_rate(cpu, 1000000000);
clk_set_rate(mck0, initial_freq);
/ ... */

However, it has been proposed in [1] to use the generic cpufreq-dt driver
and to overload the necessary clocks operations in the proper clock
driver.

To address this proposal the master clock prescaller registers a clock
notifier that will update properly the master clock divider on
PRE_RATE_CHANGE and POST_RATE_CHANGE events to avoid master clock
divider over/under clocking and also to preserve its initial value
(200MHz).

[1] https://lore.kernel.org/lkml/1609842147-8161-1-git-send-email-claudiu.beznea@microchip.com/

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/clk/at91/at91rm9200.c  |   2 +-
 drivers/clk/at91/at91sam9260.c |   2 +-
 drivers/clk/at91/at91sam9g45.c |   2 +-
 drivers/clk/at91/at91sam9n12.c |   2 +-
 drivers/clk/at91/at91sam9rl.c  |   2 +-
 drivers/clk/at91/at91sam9x5.c  |   2 +-
 drivers/clk/at91/clk-master.c  | 187 ++++++++++++++++++++-------------
 drivers/clk/at91/dt-compat.c   |   2 +-
 drivers/clk/at91/pmc.h         |   3 +-
 drivers/clk/at91/sam9x60.c     |   2 +-
 drivers/clk/at91/sama5d2.c     |   2 +-
 drivers/clk/at91/sama5d3.c     |   2 +-
 drivers/clk/at91/sama5d4.c     |   2 +-
 drivers/clk/at91/sama7g5.c     |   2 +-
 14 files changed, 130 insertions(+), 84 deletions(-)

diff --git a/drivers/clk/at91/at91rm9200.c b/drivers/clk/at91/at91rm9200.c
index 0fad1009f315..749c0b1e9c3d 100644
--- a/drivers/clk/at91/at91rm9200.c
+++ b/drivers/clk/at91/at91rm9200.c
@@ -144,7 +144,7 @@ static void __init at91rm9200_pmc_setup(struct device_node *np)
 					   &at91rm9200_master_layout,
 					   &rm9200_mck_characteristics,
 					   &rm9200_mck_lock, CLK_SET_RATE_GATE,
-					   INT_MIN);
+					   INT_MIN, 0);
 	if (IS_ERR(hw))
 		goto err_free;
 
diff --git a/drivers/clk/at91/at91sam9260.c b/drivers/clk/at91/at91sam9260.c
index ceb5495f723a..c01d9455f6d4 100644
--- a/drivers/clk/at91/at91sam9260.c
+++ b/drivers/clk/at91/at91sam9260.c
@@ -420,7 +420,7 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
 					   &at91rm9200_master_layout,
 					   data->mck_characteristics,
 					   &at91sam9260_mck_lock,
-					   CLK_SET_RATE_GATE, INT_MIN);
+					   CLK_SET_RATE_GATE, INT_MIN, 0);
 	if (IS_ERR(hw))
 		goto err_free;
 
diff --git a/drivers/clk/at91/at91sam9g45.c b/drivers/clk/at91/at91sam9g45.c
index 0214333dedd3..8364e7a06cfc 100644
--- a/drivers/clk/at91/at91sam9g45.c
+++ b/drivers/clk/at91/at91sam9g45.c
@@ -155,7 +155,7 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)
 					   &at91rm9200_master_layout,
 					   &mck_characteristics,
 					   &at91sam9g45_mck_lock,
-					   CLK_SET_RATE_GATE, INT_MIN);
+					   CLK_SET_RATE_GATE, INT_MIN, 0);
 	if (IS_ERR(hw))
 		goto err_free;
 
diff --git a/drivers/clk/at91/at91sam9n12.c b/drivers/clk/at91/at91sam9n12.c
index f9db5316a7f1..ddcae243a4ea 100644
--- a/drivers/clk/at91/at91sam9n12.c
+++ b/drivers/clk/at91/at91sam9n12.c
@@ -182,7 +182,7 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
 					   &at91sam9x5_master_layout,
 					   &mck_characteristics,
 					   &at91sam9n12_mck_lock,
-					   CLK_SET_RATE_GATE, INT_MIN);
+					   CLK_SET_RATE_GATE, INT_MIN, 0);
 	if (IS_ERR(hw))
 		goto err_free;
 
diff --git a/drivers/clk/at91/at91sam9rl.c b/drivers/clk/at91/at91sam9rl.c
index 66736e03cfef..e87b50aaa9a2 100644
--- a/drivers/clk/at91/at91sam9rl.c
+++ b/drivers/clk/at91/at91sam9rl.c
@@ -124,7 +124,7 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
 					   &at91rm9200_master_layout,
 					   &sam9rl_mck_characteristics,
 					   &sam9rl_mck_lock, CLK_SET_RATE_GATE,
-					   INT_MIN);
+					   INT_MIN, 0);
 	if (IS_ERR(hw))
 		goto err_free;
 
diff --git a/drivers/clk/at91/at91sam9x5.c b/drivers/clk/at91/at91sam9x5.c
index 79b9d3667228..4b6819f003be 100644
--- a/drivers/clk/at91/at91sam9x5.c
+++ b/drivers/clk/at91/at91sam9x5.c
@@ -202,7 +202,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
 					   parent_names,
 					   &at91sam9x5_master_layout,
 					   &mck_characteristics, &mck_lock,
-					   CLK_SET_RATE_GATE, INT_MIN);
+					   CLK_SET_RATE_GATE, INT_MIN, 0);
 	if (IS_ERR(hw))
 		goto err_free;
 
diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c
index a6a393bb1def..5e78f0416f05 100644
--- a/drivers/clk/at91/clk-master.c
+++ b/drivers/clk/at91/clk-master.c
@@ -5,6 +5,7 @@
 
 #include <linux/clk-provider.h>
 #include <linux/clkdev.h>
+#include <linux/clk.h>
 #include <linux/clk/at91_pmc.h>
 #include <linux/of.h>
 #include <linux/mfd/syscon.h>
@@ -38,6 +39,9 @@ struct clk_master {
 	u8 div;
 };
 
+/* MCK div reference to be used by notifier. */
+static struct clk_master *master_div;
+
 static inline bool clk_master_ready(struct clk_master *master)
 {
 	unsigned int bit = master->id ? AT91_PMC_MCKXRDY : AT91_PMC_MCKRDY;
@@ -153,107 +157,127 @@ static const struct clk_ops master_div_ops = {
 	.restore_context = clk_master_div_restore_context,
 };
 
-static int clk_master_div_set_rate(struct clk_hw *hw, unsigned long rate,
-				   unsigned long parent_rate)
+static int clk_master_div_set(struct clk_master *master,
+			      unsigned long parent_rate, int div)
 {
-	struct clk_master *master = to_clk_master(hw);
 	const struct clk_master_characteristics *characteristics =
 						master->characteristics;
-	unsigned long flags;
-	unsigned int mckr, tmp;
-	int div, i;
+	unsigned long rate = parent_rate;
+	unsigned int max_div = 0, div_index = 0, max_div_index = 0;
+	unsigned int i, mckr, tmp;
 	int ret;
 
-	div = DIV_ROUND_CLOSEST(parent_rate, rate);
-	if (div > ARRAY_SIZE(characteristics->divisors))
-		return -EINVAL;
-
 	for (i = 0; i < ARRAY_SIZE(characteristics->divisors); i++) {
 		if (!characteristics->divisors[i])
 			break;
 
-		if (div == characteristics->divisors[i]) {
-			div = i;
-			break;
+		if (div == characteristics->divisors[i])
+			div_index = i;
+
+		if (max_div < characteristics->divisors[i]) {
+			max_div = characteristics->divisors[i];
+			max_div_index = i;
 		}
 	}
 
-	if (i == ARRAY_SIZE(characteristics->divisors))
-		return -EINVAL;
+	if (div > max_div)
+		div_index = max_div_index;
 
-	spin_lock_irqsave(master->lock, flags);
 	ret = regmap_read(master->regmap, master->layout->offset, &mckr);
 	if (ret)
-		goto unlock;
+		return ret;
 
 	tmp = mckr & master->layout->mask;
 	tmp = (tmp >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
-	if (tmp == div)
-		goto unlock;
+	if (tmp == div_index)
+		return 0;
+
+	rate /= characteristics->divisors[div_index];
+	if (rate < characteristics->output.min)
+		pr_warn("master clk div is underclocked");
+	else if (rate > characteristics->output.max)
+		pr_warn("master clk div is overclocked");
 
 	mckr &= ~(MASTER_DIV_MASK << MASTER_DIV_SHIFT);
-	mckr |= (div << MASTER_DIV_SHIFT);
+	mckr |= (div_index << MASTER_DIV_SHIFT);
 	ret = regmap_write(master->regmap, master->layout->offset, mckr);
 	if (ret)
-		goto unlock;
+		return ret;
 
 	while (!clk_master_ready(master))
 		cpu_relax();
-unlock:
-	spin_unlock_irqrestore(master->lock, flags);
+
+	master->div = characteristics->divisors[div_index];
 
 	return 0;
 }
 
-static int clk_master_div_determine_rate(struct clk_hw *hw,
-					 struct clk_rate_request *req)
+static int at91_clk_master_div_notifier(struct notifier_block *notifier,
+					unsigned long code, void *data)
 {
-	struct clk_master *master = to_clk_master(hw);
 	const struct clk_master_characteristics *characteristics =
-						master->characteristics;
-	struct clk_hw *parent;
-	unsigned long parent_rate, tmp_rate, best_rate = 0;
-	int i, best_diff = INT_MIN, tmp_diff;
-
-	parent = clk_hw_get_parent(hw);
-	if (!parent)
-		return -EINVAL;
-
-	parent_rate = clk_hw_get_rate(parent);
-	if (!parent_rate)
-		return -EINVAL;
-
-	for (i = 0; i < ARRAY_SIZE(characteristics->divisors); i++) {
-		if (!characteristics->divisors[i])
-			break;
-
-		tmp_rate = DIV_ROUND_CLOSEST_ULL(parent_rate,
-						 characteristics->divisors[i]);
-		tmp_diff = abs(tmp_rate - req->rate);
+						master_div->characteristics;
+	struct clk_notifier_data *cnd = data;
+	unsigned long cur_rate, flags;
+	unsigned int mckr, cur_div, diff_div;
+	static unsigned int new_div;
+
+	/*
+	 * Need to increase/decrease div to avoid master div clock over/under
+	 * clocking
+	 */
+	spin_lock_irqsave(master_div->lock, flags);
+	switch (code) {
+	case PRE_RATE_CHANGE:
+		regmap_read(master_div->regmap, master_div->layout->offset,
+			    &mckr);
+		mckr &= master_div->layout->mask;
+		mckr = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
+
+		cur_div = characteristics->divisors[mckr];
+		cur_rate = DIV_ROUND_CLOSEST_ULL(cnd->old_rate, cur_div);
+		new_div = DIV_ROUND_CLOSEST(cnd->new_rate, cur_rate);
+		diff_div = abs(cur_div - new_div);
+
+		clk_master_div_set(master_div, cnd->old_rate,
+				   cur_div + diff_div);
+		break;
+
+	case POST_RATE_CHANGE:
+		clk_master_div_set(master_div, cnd->new_rate, new_div);
+		break;
+
+	default:
+		break;
+	}
+	spin_unlock_irqrestore(master_div->lock, flags);
 
-		if (!best_rate || best_diff > tmp_diff) {
-			best_diff = tmp_diff;
-			best_rate = tmp_rate;
-		}
+	return 0;
+}
 
-		if (!best_diff)
-			break;
-	}
+static struct notifier_block clk_master_notifier = {
+	.notifier_call = at91_clk_master_div_notifier,
+};
 
-	req->best_parent_rate = best_rate;
-	req->best_parent_hw = parent;
-	req->rate = best_rate;
+static unsigned long clk_master_div_recalc_rate_chg(struct clk_hw *hw,
+						    unsigned long parent_rate)
+{
+	struct clk_master *master = to_clk_master(hw);
 
-	return 0;
+	return DIV_ROUND_CLOSEST_ULL(parent_rate, master->div);
 }
 
 static void clk_master_div_restore_context_chg(struct clk_hw *hw)
 {
 	struct clk_master *master = to_clk_master(hw);
+	unsigned long flags;
 	int ret;
 
-	ret = clk_master_div_set_rate(hw, master->pms.rate,
-				      master->pms.parent_rate);
+	spin_lock_irqsave(master->lock, flags);
+	ret = clk_master_div_set(master, master->pms.parent_rate,
+				 DIV_ROUND_CLOSEST(master->pms.parent_rate,
+						   master->pms.rate));
+	spin_unlock_irqrestore(master->lock, flags);
 	if (ret)
 		pr_warn("Failed to restore MCK DIV clock\n");
 }
@@ -261,9 +285,7 @@ static void clk_master_div_restore_context_chg(struct clk_hw *hw)
 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,
+	.recalc_rate = clk_master_div_recalc_rate_chg,
 	.save_context = clk_master_div_save_context,
 	.restore_context = clk_master_div_restore_context_chg,
 };
@@ -488,6 +510,8 @@ at91_clk_register_master_internal(struct regmap *regmap,
 	struct clk_master *master;
 	struct clk_init_data init;
 	struct clk_hw *hw;
+	unsigned int mckr;
+	unsigned long irqflags;
 	int ret;
 
 	if (!name || !num_parents || !parent_names || !lock)
@@ -510,6 +534,16 @@ at91_clk_register_master_internal(struct regmap *regmap,
 	master->chg_pid = chg_pid;
 	master->lock = lock;
 
+	if (ops == &master_div_ops_chg) {
+		spin_lock_irqsave(master->lock, irqflags);
+		regmap_read(master->regmap, master->layout->offset, &mckr);
+		spin_unlock_irqrestore(master->lock, irqflags);
+
+		mckr &= layout->mask;
+		mckr = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
+		master->div = characteristics->divisors[mckr];
+	}
+
 	hw = &master->hw;
 	ret = clk_hw_register(NULL, &master->hw);
 	if (ret) {
@@ -526,19 +560,24 @@ at91_clk_register_master_pres(struct regmap *regmap,
 		const char **parent_names,
 		const struct clk_master_layout *layout,
 		const struct clk_master_characteristics *characteristics,
-		spinlock_t *lock, u32 flags, int chg_pid)
+		spinlock_t *lock, u32 flags, int chg_pid, bool notifier)
 {
 	const struct clk_ops *ops;
+	struct clk_hw *hw;
 
 	if (flags & CLK_SET_RATE_GATE)
 		ops = &master_pres_ops;
 	else
 		ops = &master_pres_ops_chg;
 
-	return at91_clk_register_master_internal(regmap, name, num_parents,
-						 parent_names, layout,
-						 characteristics, ops,
-						 lock, flags, chg_pid);
+	hw = at91_clk_register_master_internal(regmap, name, num_parents,
+					       parent_names, layout,
+					       characteristics, ops,
+					       lock, flags, chg_pid);
+	if (!IS_ERR(hw) && notifier)
+		clk_notifier_register(hw->clk, &clk_master_notifier);
+
+	return hw;
 }
 
 struct clk_hw * __init
@@ -549,16 +588,22 @@ at91_clk_register_master_div(struct regmap *regmap,
 		spinlock_t *lock, u32 flags)
 {
 	const struct clk_ops *ops;
+	struct clk_hw *hw;
 
 	if (flags & CLK_SET_RATE_GATE)
 		ops = &master_div_ops;
 	else
 		ops = &master_div_ops_chg;
 
-	return at91_clk_register_master_internal(regmap, name, 1,
-						 &parent_name, layout,
-						 characteristics, ops,
-						 lock, flags, -EINVAL);
+	hw = at91_clk_register_master_internal(regmap, name, 1,
+					       &parent_name, layout,
+					       characteristics, ops,
+					       lock, flags, -EINVAL);
+
+	if (!IS_ERR(hw))
+		master_div = to_clk_master(hw);
+
+	return hw;
 }
 
 static unsigned long
diff --git a/drivers/clk/at91/dt-compat.c b/drivers/clk/at91/dt-compat.c
index a97b99c2dc12..99ad43ea6169 100644
--- a/drivers/clk/at91/dt-compat.c
+++ b/drivers/clk/at91/dt-compat.c
@@ -393,7 +393,7 @@ of_at91_clk_master_setup(struct device_node *np,
 	hw = at91_clk_register_master_pres(regmap, "masterck_pres", num_parents,
 					   parent_names, layout,
 					   characteristics, &mck_lock,
-					   CLK_SET_RATE_GATE, INT_MIN);
+					   CLK_SET_RATE_GATE, INT_MIN, 0);
 	if (IS_ERR(hw))
 		goto out_free_characteristics;
 
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 86580ebd9ad9..2263f4df3a82 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -175,7 +175,8 @@ at91_clk_register_master_pres(struct regmap *regmap, const char *name,
 			      int num_parents, const char **parent_names,
 			      const struct clk_master_layout *layout,
 			      const struct clk_master_characteristics *characteristics,
-			      spinlock_t *lock, u32 flags, int chg_pid);
+			      spinlock_t *lock, u32 flags, int chg_pid,
+			      bool notifier);
 
 struct clk_hw * __init
 at91_clk_register_master_div(struct regmap *regmap, const char *name,
diff --git a/drivers/clk/at91/sam9x60.c b/drivers/clk/at91/sam9x60.c
index 5f6fa89571b7..8e1b89e05724 100644
--- a/drivers/clk/at91/sam9x60.c
+++ b/drivers/clk/at91/sam9x60.c
@@ -272,7 +272,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
 	hw = at91_clk_register_master_pres(regmap, "masterck_pres", 3,
 					   parent_names, &sam9x60_master_layout,
 					   &mck_characteristics, &mck_lock,
-					   CLK_SET_RATE_GATE, INT_MIN);
+					   CLK_SET_RATE_GATE, INT_MIN, 0);
 	if (IS_ERR(hw))
 		goto err_free;
 
diff --git a/drivers/clk/at91/sama5d2.c b/drivers/clk/at91/sama5d2.c
index 9a5cbc7cd55a..406d4b33562e 100644
--- a/drivers/clk/at91/sama5d2.c
+++ b/drivers/clk/at91/sama5d2.c
@@ -241,7 +241,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
 					   parent_names,
 					   &at91sam9x5_master_layout,
 					   &mck_characteristics, &mck_lock,
-					   CLK_SET_RATE_GATE, INT_MIN);
+					   CLK_SET_RATE_GATE, INT_MIN, 0);
 	if (IS_ERR(hw))
 		goto err_free;
 
diff --git a/drivers/clk/at91/sama5d3.c b/drivers/clk/at91/sama5d3.c
index 87009ee8effc..164a080c53fa 100644
--- a/drivers/clk/at91/sama5d3.c
+++ b/drivers/clk/at91/sama5d3.c
@@ -176,7 +176,7 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
 					   parent_names,
 					   &at91sam9x5_master_layout,
 					   &mck_characteristics, &mck_lock,
-					   CLK_SET_RATE_GATE, INT_MIN);
+					   CLK_SET_RATE_GATE, INT_MIN, 0);
 	if (IS_ERR(hw))
 		goto err_free;
 
diff --git a/drivers/clk/at91/sama5d4.c b/drivers/clk/at91/sama5d4.c
index 57fff790188b..9253aa2e6039 100644
--- a/drivers/clk/at91/sama5d4.c
+++ b/drivers/clk/at91/sama5d4.c
@@ -191,7 +191,7 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
 					   parent_names,
 					   &at91sam9x5_master_layout,
 					   &mck_characteristics, &mck_lock,
-					   CLK_SET_RATE_GATE, INT_MIN);
+					   CLK_SET_RATE_GATE, INT_MIN, 0);
 	if (IS_ERR(hw))
 		goto err_free;
 
diff --git a/drivers/clk/at91/sama7g5.c b/drivers/clk/at91/sama7g5.c
index 28e26fb90417..3bb630ac0f53 100644
--- a/drivers/clk/at91/sama7g5.c
+++ b/drivers/clk/at91/sama7g5.c
@@ -986,7 +986,7 @@ static void __init sama7g5_pmc_setup(struct device_node *np)
 	hw = at91_clk_register_master_pres(regmap, "cpuck", 1, parent_names,
 					   &mck0_layout, &mck0_characteristics,
 					   &pmc_mck0_lock,
-					   CLK_SET_RATE_PARENT, 0);
+					   CLK_SET_RATE_PARENT, 0, 1);
 	if (IS_ERR(hw))
 		goto err_free;
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [RESEND PATCH 1/5] clk: at91: re-factor clocks suspend/resume
  2021-03-24  9:43 ` [RESEND PATCH 1/5] clk: at91: re-factor clocks suspend/resume Claudiu Beznea
@ 2021-03-30 17:14   ` Nicolas Ferre
  2021-03-31  8:47     ` Claudiu.Beznea
  0 siblings, 1 reply; 11+ messages in thread
From: Nicolas Ferre @ 2021-03-30 17:14 UTC (permalink / raw)
  To: Claudiu Beznea, mturquette, sboyd, alexandre.belloni, ludovic.desroches
  Cc: linux-clk, linux-arm-kernel, linux-kernel

On 24/03/2021 at 10:43, Claudiu Beznea wrote:
> 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 with SAMA5D2's PMC
> in mind. SAMA7G's 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 <claudiu.beznea@microchip.com>
> ---
>   drivers/clk/at91/clk-generated.c    |  45 +++++--
>   drivers/clk/at91/clk-main.c         |  66 ++++++++++
>   drivers/clk/at91/clk-master.c       | 183 ++++++++++++++++++++++++++--
>   drivers/clk/at91/clk-peripheral.c   |  38 +++++-
>   drivers/clk/at91/clk-pll.c          |  37 ++++++
>   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         |  47 ++++++-
>   drivers/clk/at91/pmc.c              | 149 ++--------------------
>   drivers/clk/at91/pmc.h              |  24 ++--
>   12 files changed, 554 insertions(+), 179 deletions(-)
> 
> diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
> index b4fc8d71daf2..0e436f9e7508 100644
> --- 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,34 @@ 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);
> -
>   	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 | (status ? AT91_PMC_PCR_GCKEN : 0),


update_bits is already a compact version of read/modify/write, I think 
it doesn't need another lever of density.
I wouldn't include this test operator here.

>   			   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);
> +			   (status ? AT91_PMC_PCR_GCKEN : 0));

Ditto

>   	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;
>   }
>   
> @@ -239,6 +249,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,
> @@ -248,6 +275,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,
>   };
>   
>   /**
> @@ -314,8 +343,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;
> diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c
> index cfae2f59df66..8601b27c1ae0 100644
> --- 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(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
> @@ -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
> @@ -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
> diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c
> index a80427980bf7..6f1fb2eb2a8d 100644
> --- 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_recalc_rate(struct clk_hw *hw,
>   	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");

Just to be sure: I see no write to register in this function and only 
warning print. I assume it's intended.

> +}
> +
>   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(struct clk_hw *hw, unsigned long rate,
>   	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(struct clk_hw *hw, unsigned long rate,
>   		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(struct clk_hw *hw,
>   	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(struct clk_hw *hw, unsigned long rate,
>   {
>   	struct clk_master *master = to_clk_master(hw);
>   	unsigned long flags;
> -	unsigned int pres;
> +	unsigned int pres, val;
> +	int ret;
>   
>   	pres = DIV_ROUND_CLOSEST(parent_rate, rate);
>   	if (pres > MASTER_PRES_MAX)
> @@ -284,15 +356,24 @@ static int clk_master_pres_set_rate(struct clk_hw *hw, unsigned long rate,
>   		pres = ffs(pres) - 1;
>   
>   	spin_lock_irqsave(master->lock, flags);
> +	ret = regmap_read(master->regmap, master->layout->offset, &val);
> +	if (ret)
> +		goto unlock;
> +
> +	val = (val >> master->layout->pres_shift) & MASTER_PRES_MASK;
> +	if (pres == val)
> +		goto unlock;
> +
>   	regmap_update_bits(master->regmap, master->layout->offset,
>   			   (MASTER_PRES_MASK << master->layout->pres_shift),
>   			   (pres << master->layout->pres_shift));
>   
>   	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 +411,66 @@ static u8 clk_master_pres_get_parent(struct clk_hw *hw)
>   	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);
> +
> +	pres = (val >> master->layout->pres_shift) & MASTER_PRES_MASK;
> +	if (pres == 3 && 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);
> +
> +	pres = (val >> master->layout->pres_shift) & MASTER_PRES_MASK;
> +	if (pres == 3 && 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");

Same here: it's restored by bootloader, right?

> +}
> +
> +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 +480,8 @@ static const struct clk_ops master_pres_ops_chg = {
>   	.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,9 +677,9 @@ static int clk_sama7g5_master_set_parent(struct clk_hw *hw, u8 index)
>   	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;
>   
> @@ -550,9 +688,10 @@ static int clk_sama7g5_master_enable(struct clk_hw *hw)
>   	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 |
> +			   (status ? PMC_MCR_EN : 0) | PMC_MCR_CSS | PMC_MCR_DIV |

Ditto

>   			   PMC_MCR_CMD | PMC_MCR_ID_MSK,
> -			   PMC_MCR_EN | (master->parent << PMC_MCR_CSS_SHIFT) |
> +			   (status ? PMC_MCR_EN : 0) |
> +			   (master->parent << PMC_MCR_CSS_SHIFT) |
>   			   (master->div << MASTER_DIV_SHIFT) |

Ditto

>   			   PMC_MCR_CMD | PMC_MCR_ID(master->id));

This whole line becomes hardly readable.

>   
> @@ -563,6 +702,13 @@ static int clk_sama7g5_master_enable(struct clk_hw *hw)
>   		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 +766,23 @@ static int clk_sama7g5_master_set_rate(struct clk_hw *hw, unsigned long rate,
>   	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 +792,8 @@ static const struct clk_ops sama7g5_master_ops = {
>   	.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
> diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c
> index 7a27ba8e0577..bf088cd06225 100644
> --- 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,9 +156,9 @@ static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph)
>   	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;
>   
>   	if (periph->id < PERIPHERAL_ID_MIN)
> @@ -168,15 +169,22 @@ static int clk_sam9x5_peripheral_enable(struct clk_hw *hw)
>   		     (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,
> +			   (status ? AT91_PMC_PCR_EN : 0),
>   			   field_prep(periph->layout->div_mask, periph->div) |
>   			   periph->layout->cmd |
> -			   AT91_PMC_PCR_EN);
> +			   (status ? AT91_PMC_PCR_EN : 0));

Ditto

>   	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_rate(struct clk_hw *hw,
>   	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_peripheral_ops = {
>   	.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_peripheral_chg_ops = {
>   	.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(struct regmap *regmap, spinlock_t *lock,
>   		hw = ERR_PTR(ret);
>   	} else {
>   		clk_sam9x5_peripheral_autodiv(periph);
> -		pmc_register_id(id);
>   	}
>   
>   	return hw;
> diff --git a/drivers/clk/at91/clk-pll.c b/drivers/clk/at91/clk-pll.c
> index 6ed986d3eee0..31964622c658 100644
> --- 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,40 @@ static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
>   	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;
> +	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);
> +
> +	if (pll->pms.rate != calc_rate ||
> +	    pll->pms.status != clk_pll_ready(pll->regmap, PLL_REG(pll->id)) ||
> +	    (((pllr >> PLL_COUNT_SHIFT) & PLL_MAX_COUNT) != PLL_MAX_COUNT) ||
> +	    (out && ((pllr >> PLL_OUT_SHIFT) & 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 +302,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
> diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c
> index fcf8f6a1c2c6..6c4b259d31d3 100644
> --- 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(struct clk_hw *hw, unsigned long rate,
>   	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 regmap *regmap,
>   	if (ret) {
>   		kfree(prog);
>   		hw = ERR_PTR(ret);
> -	} else {
> -		pmc_register_pck(id);
>   	}
>   
>   	return hw;
> diff --git a/drivers/clk/at91/clk-sam9x60-pll.c b/drivers/clk/at91/clk-sam9x60-pll.c
> index 34e3ab13741a..7020d3bf6e13 100644
> --- 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_recalc_rate(struct clk_hw *hw,
>   		((u64)parent_rate * frac->frac >> 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 @@ static int sam9x60_frac_pll_prepare(struct clk_hw *hw)
>   	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 @@ static int sam9x60_frac_pll_set_rate_chg(struct clk_hw *hw, unsigned long rate,
>   	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_pll_ops = {
>   	.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_pll_ops_chg = {
>   	.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 @@ static int sam9x60_div_pll_prepare(struct clk_hw *hw)
>   	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 @@ static int sam9x60_div_pll_set_rate_chg(struct clk_hw *hw, unsigned long rate,
>   	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_pll_ops = {
>   	.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_pll_ops_chg = {
>   	.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
> diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c
> index f83ec0de86c3..80720fd1a9cf 100644
> --- 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 clk_hw *hw)
>   	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
> diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c
> index 31d5c45e30d7..b0696a928aa9 100644
> --- 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(struct clk_hw *hw, unsigned long rate,
>   	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)
> diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c
> index df9f3fc3b6a6..2b102ee2a7d3 100644
> --- 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_rate(struct clk_hw *hw,
>   	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,35 @@ static int clk_utmi_sama7g5_is_prepared(struct clk_hw *hw)
>   	return 0;
>   }
>   
> +static unsigned long clk_utmi_sama7g5_recalc_rate(struct clk_hw *hw,
> +						  unsigned long parent_rate)
> +{
> +	return 480000000;
> +}
> +
> +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,
> +	.recalc_rate = clk_utmi_sama7g5_recalc_rate,
> +	.save_context = clk_utmi_sama7g5_save_context,
> +	.restore_context = clk_utmi_sama7g5_restore_context,
>   };
>   
>   struct clk_hw * __init
> diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
> index 20ee9dccee78..77b57c9f5dcb 100644
> --- a/drivers/clk/at91/pmc.c
> +++ b/drivers/clk/at91/pmc.c
> @@ -3,6 +3,7 @@
>    *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
>    */
>   
> +#include <linux/clk.h>
>   #include <linux/clk-provider.h>
>   #include <linux/clkdev.h>
>   #include <linux/clk/at91_pmc.h>
> @@ -14,8 +15,6 @@
>   
>   #include <asm/proc-fns.h>
>   
> -#include <dt-bindings/clock/at91.h>
> -
>   #include "pmc.h"
>   
>   #define PMC_MAX_IDS 128
> @@ -111,147 +110,19 @@ struct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem,
>   }
>   
>   #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)
> -{
> -	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;
> -	}
> -}
> -
> -/*
> - * 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)
> -{
> -	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)
> +static int at91_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;
> +	return clk_save_context();
>   }
>   
> -static bool pmc_ready(unsigned int mask)
> +static void at91_pmc_resume(void)
>   {
> -	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();
>   }

I like how it simplify this part! Good.

>   
>   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[] = {
> @@ -265,12 +136,8 @@ static int __init pmc_register_ops(void)
>   
>   	np = of_find_matching_node(NULL, sama5d2_pmc_dt_ids);
>   	if (!np)
> -		return -ENODEV;
> -
> -	pmcreg = device_node_to_regmap(np);
> +		return 0;
>   	of_node_put(np);
> -	if (IS_ERR(pmcreg))
> -		return PTR_ERR(pmcreg);
>   
>   	register_syscore_ops(&pmc_syscore_ops);
>   
> diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
> index a49076c804a9..86580ebd9ad9 100644
> --- a/drivers/clk/at91/pmc.h
> +++ b/drivers/clk/at91/pmc.h
> @@ -13,6 +13,8 @@
>   #include <linux/regmap.h>
>   #include <linux/spinlock.h>
>   
> +#include <dt-bindings/clock/at91.h>
> +
>   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
> + * @status: clock status (enabled or disabled)
> + * @parent: clock parent index
> + * @parent_rate: clock parent rate
> + * @rate: clock rate
> + */
> +struct at91_clk_pms {
> +	unsigned int status;
> +	unsigned int parent;
> +	unsigned long parent_rate;
> +	unsigned long rate;
> +};
> +
>   #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_ */
> 


-- 
Nicolas Ferre

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RESEND PATCH 4/5] clk: at91: clk-master: add register definition for sama7g5's master clock
  2021-03-24  9:43 ` [RESEND PATCH 4/5] clk: at91: clk-master: add register definition for sama7g5's master clock Claudiu Beznea
@ 2021-03-30 17:23   ` Nicolas Ferre
  0 siblings, 0 replies; 11+ messages in thread
From: Nicolas Ferre @ 2021-03-30 17:23 UTC (permalink / raw)
  To: Claudiu Beznea, mturquette, sboyd, alexandre.belloni, ludovic.desroches
  Cc: linux-clk, linux-arm-kernel, linux-kernel

On 24/03/2021 at 10:43, Claudiu Beznea wrote:
> Add register definitions for SAMA7G5's master clock. These would be
> also used by architecture specific power saving code. With this, update
> also clk-master.c.
> 
> Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
> ---
>   drivers/clk/at91/clk-master.c | 51 +++++++++++++++++------------------
>   include/linux/clk/at91_pmc.h  | 26 ++++++++++++++++++
>   2 files changed, 50 insertions(+), 27 deletions(-)
> 
> diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c
> index 6f1fb2eb2a8d..a6a393bb1def 100644
> --- a/drivers/clk/at91/clk-master.c
> +++ b/drivers/clk/at91/clk-master.c
> @@ -17,15 +17,7 @@
>   #define MASTER_DIV_SHIFT	8
>   #define MASTER_DIV_MASK		0x7
>   
> -#define PMC_MCR			0x30
> -#define PMC_MCR_ID_MSK		GENMASK(3, 0)
> -#define PMC_MCR_CMD		BIT(7)
> -#define PMC_MCR_DIV		GENMASK(10, 8)
> -#define PMC_MCR_CSS		GENMASK(20, 16)
>   #define PMC_MCR_CSS_SHIFT	(16)
> -#define PMC_MCR_EN		BIT(28)
> -
> -#define PMC_MCR_ID(x)		((x) & PMC_MCR_ID_MSK)
>   
>   #define MASTER_MAX_ID		4
>   
> @@ -685,17 +677,20 @@ static void clk_sama7g5_master_set(struct clk_master *master,
>   
>   	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,
> -			   (status ? PMC_MCR_EN : 0) | PMC_MCR_CSS | PMC_MCR_DIV |
> -			   PMC_MCR_CMD | PMC_MCR_ID_MSK,
> -			   (status ? PMC_MCR_EN : 0) |
> +	regmap_write(master->regmap, AT91_PMC_MCR_V2,
> +		     AT91_PMC_MCR_V2_ID(master->id));
> +	regmap_read(master->regmap, AT91_PMC_MCR_V2, &val);
> +	regmap_update_bits(master->regmap, AT91_PMC_MCR_V2,
> +			   (status ? AT91_PMC_MCR_V2_EN : 0) |
> +			   AT91_PMC_MCR_V2_CSS | AT91_PMC_MCR_V2_DIV |
> +			   AT91_PMC_MCR_V2_CMD | AT91_PMC_MCR_V2_ID_MSK,
> +			   (status ? AT91_PMC_MCR_V2_EN : 0) |

Ok, here also. Can't we make it simpler to read?

>   			   (master->parent << PMC_MCR_CSS_SHIFT) |
>   			   (master->div << MASTER_DIV_SHIFT) |
> -			   PMC_MCR_CMD | PMC_MCR_ID(master->id));
> +			   AT91_PMC_MCR_V2_CMD |
> +			   AT91_PMC_MCR_V2_ID(master->id));
>   
> -	cparent = (val & PMC_MCR_CSS) >> PMC_MCR_CSS_SHIFT;
> +	cparent = (val & AT91_PMC_MCR_V2_CSS) >> PMC_MCR_CSS_SHIFT;
>   
>   	/* Wait here only if parent is being changed. */
>   	while ((cparent != master->parent) && !clk_master_ready(master))
> @@ -720,10 +715,12 @@ static void clk_sama7g5_master_disable(struct clk_hw *hw)
>   
>   	spin_lock_irqsave(master->lock, flags);
>   
> -	regmap_write(master->regmap, PMC_MCR, master->id);
> -	regmap_update_bits(master->regmap, PMC_MCR,
> -			   PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK,
> -			   PMC_MCR_CMD | PMC_MCR_ID(master->id));
> +	regmap_write(master->regmap, AT91_PMC_MCR_V2, master->id);
> +	regmap_update_bits(master->regmap, AT91_PMC_MCR_V2,
> +			   AT91_PMC_MCR_V2_EN | AT91_PMC_MCR_V2_CMD |
> +			   AT91_PMC_MCR_V2_ID_MSK,
> +			   AT91_PMC_MCR_V2_CMD |
> +			   AT91_PMC_MCR_V2_ID(master->id));
>   
>   	spin_unlock_irqrestore(master->lock, flags);
>   }
> @@ -736,12 +733,12 @@ static int clk_sama7g5_master_is_enabled(struct clk_hw *hw)
>   
>   	spin_lock_irqsave(master->lock, flags);
>   
> -	regmap_write(master->regmap, PMC_MCR, master->id);
> -	regmap_read(master->regmap, PMC_MCR, &val);
> +	regmap_write(master->regmap, AT91_PMC_MCR_V2, master->id);
> +	regmap_read(master->regmap, AT91_PMC_MCR_V2, &val);
>   
>   	spin_unlock_irqrestore(master->lock, flags);
>   
> -	return !!(val & PMC_MCR_EN);
> +	return !!(val & AT91_PMC_MCR_V2_EN);
>   }
>   
>   static int clk_sama7g5_master_set_rate(struct clk_hw *hw, unsigned long rate,
> @@ -837,10 +834,10 @@ at91_clk_sama7g5_register_master(struct regmap *regmap,
>   	master->mux_table = mux_table;
>   
>   	spin_lock_irqsave(master->lock, flags);
> -	regmap_write(master->regmap, PMC_MCR, master->id);
> -	regmap_read(master->regmap, PMC_MCR, &val);
> -	master->parent = (val & PMC_MCR_CSS) >> PMC_MCR_CSS_SHIFT;
> -	master->div = (val & PMC_MCR_DIV) >> MASTER_DIV_SHIFT;
> +	regmap_write(master->regmap, AT91_PMC_MCR_V2, master->id);
> +	regmap_read(master->regmap, AT91_PMC_MCR_V2, &val);
> +	master->parent = (val & AT91_PMC_MCR_V2_CSS) >> PMC_MCR_CSS_SHIFT;
> +	master->div = (val & AT91_PMC_MCR_V2_DIV) >> MASTER_DIV_SHIFT;
>   	spin_unlock_irqrestore(master->lock, flags);
>   
>   	hw = &master->hw;
> diff --git a/include/linux/clk/at91_pmc.h b/include/linux/clk/at91_pmc.h
> index a4f82e836a7c..ccb3f034bfa9 100644
> --- a/include/linux/clk/at91_pmc.h
> +++ b/include/linux/clk/at91_pmc.h
> @@ -137,6 +137,32 @@
>   #define			AT91_PMC_PLLADIV2_ON		(1 << 12)
>   #define		AT91_PMC_H32MXDIV	BIT(24)
>   
> +#define	AT91_PMC_MCR_V2		0x30				/* Master Clock Register [SAMA7G5 only] */
> +#define		AT91_PMC_MCR_V2_ID_MSK	(0xF)
> +#define			AT91_PMC_MCR_V2_ID(_id)		((_id) & AT91_PMC_MCR_V2_ID_MSK)
> +#define		AT91_PMC_MCR_V2_CMD	(1 << 7)
> +#define		AT91_PMC_MCR_V2_DIV	(7 << 8)
> +#define			AT91_PMC_MCR_V2_DIV1		(0 << 8)
> +#define			AT91_PMC_MCR_V2_DIV2		(1 << 8)
> +#define			AT91_PMC_MCR_V2_DIV4		(2 << 8)
> +#define			AT91_PMC_MCR_V2_DIV8		(3 << 8)
> +#define			AT91_PMC_MCR_V2_DIV16		(4 << 8)
> +#define			AT91_PMC_MCR_V2_DIV32		(5 << 8)
> +#define			AT91_PMC_MCR_V2_DIV64		(6 << 8)
> +#define			AT91_PMC_MCR_V2_DIV3		(7 << 8)
> +#define		AT91_PMC_MCR_V2_CSS	(0x1F << 16)
> +#define			AT91_PMC_MCR_V2_CSS_MD_SLCK	(0 << 16)
> +#define			AT91_PMC_MCR_V2_CSS_TD_SLCK	(1 << 16)
> +#define			AT91_PMC_MCR_V2_CSS_MAINCK	(2 << 16)
> +#define			AT91_PMC_MCR_V2_CSS_MCK0	(3 << 16)
> +#define			AT91_PMC_MCR_V2_CSS_SYSPLL	(5 << 16)
> +#define			AT91_PMC_MCR_V2_CSS_DDRPLL	(6 << 16)
> +#define			AT91_PMC_MCR_V2_CSS_IMGPLL	(7 << 16)
> +#define			AT91_PMC_MCR_V2_CSS_BAUDPLL	(8 << 16)
> +#define			AT91_PMC_MCR_V2_CSS_AUDIOPLL	(9 << 16)
> +#define			AT91_PMC_MCR_V2_CSS_ETHPLL	(10 << 16)
> +#define		AT91_PMC_MCR_V2_EN	(1 << 28)
> +
>   #define AT91_PMC_XTALF		0x34			/* Main XTAL Frequency Register [SAMA7G5 only] */
>   
>   #define	AT91_PMC_USB		0x38			/* USB Clock Register [some SAM9 only] */
> 

Autherwise, it's fine. Thanks. Best regards,
   Nicolas

-- 
Nicolas Ferre

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RESEND PATCH 3/5] clk: at91: sama7g5: add securam's peripheral clock
  2021-03-24  9:43 ` [RESEND PATCH 3/5] clk: at91: sama7g5: add securam's peripheral clock Claudiu Beznea
@ 2021-03-30 17:27   ` Nicolas Ferre
  0 siblings, 0 replies; 11+ messages in thread
From: Nicolas Ferre @ 2021-03-30 17:27 UTC (permalink / raw)
  To: Claudiu Beznea, mturquette, sboyd, alexandre.belloni, ludovic.desroches
  Cc: linux-clk, linux-arm-kernel, linux-kernel

On 24/03/2021 at 10:43, Claudiu Beznea wrote:
> Add SECURAM's peripheral clock.
> 
> Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>

Acked-by: Nicolas Ferre <nicolas.ferre@microchip.com>

> ---
>   drivers/clk/at91/sama7g5.c | 1 +
>   1 file changed, 1 insertion(+)
> 
> diff --git a/drivers/clk/at91/sama7g5.c b/drivers/clk/at91/sama7g5.c
> index a6e20b35960e..28e26fb90417 100644
> --- a/drivers/clk/at91/sama7g5.c
> +++ b/drivers/clk/at91/sama7g5.c
> @@ -377,6 +377,7 @@ static const struct {
>   	u8 id;
>   } sama7g5_periphck[] = {
>   	{ .n = "pioA_clk",	.p = "mck0", .id = 11, },
> +	{ .n = "securam_clk",	.p = "mck0", .id = 18, },
>   	{ .n = "sfr_clk",	.p = "mck1", .id = 19, },
>   	{ .n = "hsmc_clk",	.p = "mck1", .id = 21, },
>   	{ .n = "xdmac0_clk",	.p = "mck1", .id = 22, },
> 

Thanks,
   Nicolas

-- 
Nicolas Ferre

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RESEND PATCH 1/5] clk: at91: re-factor clocks suspend/resume
  2021-03-30 17:14   ` Nicolas Ferre
@ 2021-03-31  8:47     ` Claudiu.Beznea
  2021-03-31 11:37       ` Nicolas Ferre
  0 siblings, 1 reply; 11+ messages in thread
From: Claudiu.Beznea @ 2021-03-31  8:47 UTC (permalink / raw)
  To: Nicolas.Ferre, mturquette, sboyd, alexandre.belloni, Ludovic.Desroches
  Cc: linux-clk, linux-arm-kernel, linux-kernel

On 30.03.2021 20:14, Nicolas Ferre wrote:
> On 24/03/2021 at 10:43, Claudiu Beznea wrote:
>> 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 with SAMA5D2's PMC
>> in mind. SAMA7G's 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 <claudiu.beznea@microchip.com>
>> ---
>>   drivers/clk/at91/clk-generated.c    |  45 +++++--
>>   drivers/clk/at91/clk-main.c         |  66 ++++++++++
>>   drivers/clk/at91/clk-master.c       | 183 ++++++++++++++++++++++++++--
>>   drivers/clk/at91/clk-peripheral.c   |  38 +++++-
>>   drivers/clk/at91/clk-pll.c          |  37 ++++++
>>   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         |  47 ++++++-
>>   drivers/clk/at91/pmc.c              | 149 ++--------------------
>>   drivers/clk/at91/pmc.h              |  24 ++--
>>   12 files changed, 554 insertions(+), 179 deletions(-)
>>
>> diff --git a/drivers/clk/at91/clk-generated.c
>> b/drivers/clk/at91/clk-generated.c
>> index b4fc8d71daf2..0e436f9e7508 100644
>> --- 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,34 @@ 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);
>> -
>>       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 | (status ? AT91_PMC_PCR_GCKEN : 0),
> 
> 
> update_bits is already a compact version of read/modify/write, I think it
> doesn't need another lever of density.
> I wouldn't include this test operator here.

Having the check of status here allows to have a single function, namely
clk_generated_set() for enable, disable that could be used in in clock
enable/disable APIs but also on suspend/resume function. To avoid the
checking as, a first solution I see using another local variable that does
what the test operator is doing here:

unsigned int x = status ? AT91_PMC_PCR_GCKEN : 0;

and then:

     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 | x,

Would you prefer having another local variable and doing like this?


> 
>>                  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);
>> +               (status ? AT91_PMC_PCR_GCKEN : 0));
> 
> Ditto
> 
>>       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;
>>   }
>>   @@ -239,6 +249,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,
>> @@ -248,6 +275,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,
>>   };
>>     /**
>> @@ -314,8 +343,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;
>> diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c
>> index cfae2f59df66..8601b27c1ae0 100644
>> --- 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(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
>> @@ -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
>> @@ -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
>> diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c
>> index a80427980bf7..6f1fb2eb2a8d 100644
>> --- 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_recalc_rate(struct clk_hw *hw,
>>       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");
> 
> Just to be sure: I see no write to register in this function and only
> warning print. I assume it's intended.

Yes, its intended. It is the code that was active before this patch (see
old code in drivers/clk/at91/pmc.c). This register should be restored by
bootloader.

> 
>> +}
>> +
>>   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(struct clk_hw *hw,
>> unsigned long rate,
>>       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(struct clk_hw
>> *hw, unsigned long rate,
>>           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(struct
>> clk_hw *hw,
>>       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(struct clk_hw
>> *hw, unsigned long rate,
>>   {
>>       struct clk_master *master = to_clk_master(hw);
>>       unsigned long flags;
>> -    unsigned int pres;
>> +    unsigned int pres, val;
>> +    int ret;
>>         pres = DIV_ROUND_CLOSEST(parent_rate, rate);
>>       if (pres > MASTER_PRES_MAX)
>> @@ -284,15 +356,24 @@ static int clk_master_pres_set_rate(struct clk_hw
>> *hw, unsigned long rate,
>>           pres = ffs(pres) - 1;
>>         spin_lock_irqsave(master->lock, flags);
>> +    ret = regmap_read(master->regmap, master->layout->offset, &val);
>> +    if (ret)
>> +        goto unlock;
>> +
>> +    val = (val >> master->layout->pres_shift) & MASTER_PRES_MASK;
>> +    if (pres == val)
>> +        goto unlock;
>> +
>>       regmap_update_bits(master->regmap, master->layout->offset,
>>                  (MASTER_PRES_MASK << master->layout->pres_shift),
>>                  (pres << master->layout->pres_shift));
>>         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 +411,66 @@ static u8 clk_master_pres_get_parent(struct clk_hw
>> *hw)
>>       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);
>> +
>> +    pres = (val >> master->layout->pres_shift) & MASTER_PRES_MASK;
>> +    if (pres == 3 && 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);
>> +
>> +    pres = (val >> master->layout->pres_shift) & MASTER_PRES_MASK;
>> +    if (pres == 3 && 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");
> 
> Same here: it's restored by bootloader, right?

Yes!

> 
>> +}
>> +
>> +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 +480,8 @@ static const struct clk_ops master_pres_ops_chg = {
>>       .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,9 +677,9 @@ static int clk_sama7g5_master_set_parent(struct
>> clk_hw *hw, u8 index)
>>       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;
>>   @@ -550,9 +688,10 @@ static int clk_sama7g5_master_enable(struct clk_hw
>> *hw)
>>       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 |
>> +               (status ? PMC_MCR_EN : 0) | PMC_MCR_CSS | PMC_MCR_DIV |
> 
> Ditto
> 
>>                  PMC_MCR_CMD | PMC_MCR_ID_MSK,
>> -               PMC_MCR_EN | (master->parent << PMC_MCR_CSS_SHIFT) |
>> +               (status ? PMC_MCR_EN : 0) |
>> +               (master->parent << PMC_MCR_CSS_SHIFT) |
>>                  (master->div << MASTER_DIV_SHIFT) |
> 
> Ditto
> 
>>                  PMC_MCR_CMD | PMC_MCR_ID(master->id));
> 
> This whole line becomes hardly readable.

It is what previously has been used except the adition of status variable.
All the values passed to regmap_update_bits() needs to be there for proper
functionality. The clk_sama7g5_master_set() has been added to use the same
function, namely clk_sama7g5_master_set(), in .enable, .disable,
.save_context, .restore_context APIs.

Would you prefer using another local variable to store the conditional
checking of status variable:

unsigned int x = status ? PMC_MCR_EN : 0;

?

Thank you for your review,
Claudiu

> 
>>   @@ -563,6 +702,13 @@ static int clk_sama7g5_master_enable(struct clk_hw
>> *hw)
>>           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 +766,23 @@ static int clk_sama7g5_master_set_rate(struct clk_hw
>> *hw, unsigned long rate,
>>       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 +792,8 @@ static const struct clk_ops sama7g5_master_ops = {
>>       .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
>> diff --git a/drivers/clk/at91/clk-peripheral.c
>> b/drivers/clk/at91/clk-peripheral.c
>> index 7a27ba8e0577..bf088cd06225 100644
>> --- 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,9 +156,9 @@ static void clk_sam9x5_peripheral_autodiv(struct
>> clk_sam9x5_peripheral *periph)
>>       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;
>>         if (periph->id < PERIPHERAL_ID_MIN)
>> @@ -168,15 +169,22 @@ static int clk_sam9x5_peripheral_enable(struct
>> clk_hw *hw)
>>                (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,
>> +               (status ? AT91_PMC_PCR_EN : 0),
>>                  field_prep(periph->layout->div_mask, periph->div) |
>>                  periph->layout->cmd |
>> -               AT91_PMC_PCR_EN);
>> +               (status ? AT91_PMC_PCR_EN : 0));
> 
> Ditto
> 
>>       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_rate(struct
>> clk_hw *hw,
>>       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_peripheral_ops = {
>>       .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_peripheral_chg_ops
>> = {
>>       .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(struct regmap
>> *regmap, spinlock_t *lock,
>>           hw = ERR_PTR(ret);
>>       } else {
>>           clk_sam9x5_peripheral_autodiv(periph);
>> -        pmc_register_id(id);
>>       }
>>         return hw;
>> diff --git a/drivers/clk/at91/clk-pll.c b/drivers/clk/at91/clk-pll.c
>> index 6ed986d3eee0..31964622c658 100644
>> --- 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,40 @@ static int clk_pll_set_rate(struct clk_hw *hw,
>> unsigned long rate,
>>       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;
>> +    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);
>> +
>> +    if (pll->pms.rate != calc_rate ||
>> +        pll->pms.status != clk_pll_ready(pll->regmap, PLL_REG(pll->id)) ||
>> +        (((pllr >> PLL_COUNT_SHIFT) & PLL_MAX_COUNT) != PLL_MAX_COUNT) ||
>> +        (out && ((pllr >> PLL_OUT_SHIFT) & 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 +302,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
>> diff --git a/drivers/clk/at91/clk-programmable.c
>> b/drivers/clk/at91/clk-programmable.c
>> index fcf8f6a1c2c6..6c4b259d31d3 100644
>> --- 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(struct clk_hw
>> *hw, unsigned long rate,
>>       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 regmap *regmap,
>>       if (ret) {
>>           kfree(prog);
>>           hw = ERR_PTR(ret);
>> -    } else {
>> -        pmc_register_pck(id);
>>       }
>>         return hw;
>> diff --git a/drivers/clk/at91/clk-sam9x60-pll.c
>> b/drivers/clk/at91/clk-sam9x60-pll.c
>> index 34e3ab13741a..7020d3bf6e13 100644
>> --- 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_recalc_rate(struct clk_hw *hw,
>>           ((u64)parent_rate * frac->frac >> 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 @@ static int sam9x60_frac_pll_prepare(struct clk_hw *hw)
>>       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 @@ static int sam9x60_frac_pll_set_rate_chg(struct
>> clk_hw *hw, unsigned long rate,
>>       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_pll_ops = {
>>       .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_pll_ops_chg = {
>>       .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 @@ static int sam9x60_div_pll_prepare(struct clk_hw *hw)
>>       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 @@ static int sam9x60_div_pll_set_rate_chg(struct
>> clk_hw *hw, unsigned long rate,
>>       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_pll_ops = {
>>       .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_pll_ops_chg = {
>>       .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
>> diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c
>> index f83ec0de86c3..80720fd1a9cf 100644
>> --- 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 clk_hw *hw)
>>       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
>> diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c
>> index 31d5c45e30d7..b0696a928aa9 100644
>> --- 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(struct
>> clk_hw *hw, unsigned long rate,
>>       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)
>> diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c
>> index df9f3fc3b6a6..2b102ee2a7d3 100644
>> --- 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_rate(struct
>> clk_hw *hw,
>>       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,35 @@ static int clk_utmi_sama7g5_is_prepared(struct
>> clk_hw *hw)
>>       return 0;
>>   }
>>   +static unsigned long clk_utmi_sama7g5_recalc_rate(struct clk_hw *hw,
>> +                          unsigned long parent_rate)
>> +{
>> +    return 480000000;
>> +}
>> +
>> +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,
>> +    .recalc_rate = clk_utmi_sama7g5_recalc_rate,
>> +    .save_context = clk_utmi_sama7g5_save_context,
>> +    .restore_context = clk_utmi_sama7g5_restore_context,
>>   };
>>     struct clk_hw * __init
>> diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
>> index 20ee9dccee78..77b57c9f5dcb 100644
>> --- a/drivers/clk/at91/pmc.c
>> +++ b/drivers/clk/at91/pmc.c
>> @@ -3,6 +3,7 @@
>>    *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
>>    */
>>   +#include <linux/clk.h>
>>   #include <linux/clk-provider.h>
>>   #include <linux/clkdev.h>
>>   #include <linux/clk/at91_pmc.h>
>> @@ -14,8 +15,6 @@
>>     #include <asm/proc-fns.h>
>>   -#include <dt-bindings/clock/at91.h>
>> -
>>   #include "pmc.h"
>>     #define PMC_MAX_IDS 128
>> @@ -111,147 +110,19 @@ struct pmc_data *pmc_data_allocate(unsigned int
>> ncore, unsigned int nsystem,
>>   }
>>     #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)
>> -{
>> -    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;
>> -    }
>> -}
>> -
>> -/*
>> - * 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)
>> -{
>> -    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)
>> +static int at91_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;
>> +    return clk_save_context();
>>   }
>>   -static bool pmc_ready(unsigned int mask)
>> +static void at91_pmc_resume(void)
>>   {
>> -    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();
>>   }
> 
> I like how it simplify this part! Good.
> 
>>     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[] = {
>> @@ -265,12 +136,8 @@ static int __init pmc_register_ops(void)
>>         np = of_find_matching_node(NULL, sama5d2_pmc_dt_ids);
>>       if (!np)
>> -        return -ENODEV;
>> -
>> -    pmcreg = device_node_to_regmap(np);
>> +        return 0;
>>       of_node_put(np);
>> -    if (IS_ERR(pmcreg))
>> -        return PTR_ERR(pmcreg);
>>         register_syscore_ops(&pmc_syscore_ops);
>>   diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
>> index a49076c804a9..86580ebd9ad9 100644
>> --- a/drivers/clk/at91/pmc.h
>> +++ b/drivers/clk/at91/pmc.h
>> @@ -13,6 +13,8 @@
>>   #include <linux/regmap.h>
>>   #include <linux/spinlock.h>
>>   +#include <dt-bindings/clock/at91.h>
>> +
>>   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
>> + * @status: clock status (enabled or disabled)
>> + * @parent: clock parent index
>> + * @parent_rate: clock parent rate
>> + * @rate: clock rate
>> + */
>> +struct at91_clk_pms {
>> +    unsigned int status;
>> +    unsigned int parent;
>> +    unsigned long parent_rate;
>> +    unsigned long rate;
>> +};
>> +
>>   #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_ */
>>
> 
> 


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RESEND PATCH 1/5] clk: at91: re-factor clocks suspend/resume
  2021-03-31  8:47     ` Claudiu.Beznea
@ 2021-03-31 11:37       ` Nicolas Ferre
  0 siblings, 0 replies; 11+ messages in thread
From: Nicolas Ferre @ 2021-03-31 11:37 UTC (permalink / raw)
  To: Claudiu Beznea - M18063, mturquette, sboyd, alexandre.belloni,
	Ludovic Desroches - M43218
  Cc: linux-clk, linux-arm-kernel, linux-kernel

On 31/03/2021 at 10:47, Claudiu Beznea - M18063 wrote:
> On 30.03.2021 20:14, Nicolas Ferre wrote:
>> On 24/03/2021 at 10:43, Claudiu Beznea wrote:
>>> 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 with SAMA5D2's PMC
>>> in mind. SAMA7G's 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 <claudiu.beznea@microchip.com>
>>> ---
>>>    drivers/clk/at91/clk-generated.c    |  45 +++++--
>>>    drivers/clk/at91/clk-main.c         |  66 ++++++++++
>>>    drivers/clk/at91/clk-master.c       | 183 ++++++++++++++++++++++++++--
>>>    drivers/clk/at91/clk-peripheral.c   |  38 +++++-
>>>    drivers/clk/at91/clk-pll.c          |  37 ++++++
>>>    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         |  47 ++++++-
>>>    drivers/clk/at91/pmc.c              | 149 ++--------------------
>>>    drivers/clk/at91/pmc.h              |  24 ++--
>>>    12 files changed, 554 insertions(+), 179 deletions(-)
>>>
>>> diff --git a/drivers/clk/at91/clk-generated.c
>>> b/drivers/clk/at91/clk-generated.c
>>> index b4fc8d71daf2..0e436f9e7508 100644
>>> --- 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,34 @@ 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);
>>> -
>>>        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 | (status ? AT91_PMC_PCR_GCKEN : 0),
>>
>>
>> update_bits is already a compact version of read/modify/write, I think it
>> doesn't need another lever of density.
>> I wouldn't include this test operator here.
> 
> Having the check of status here allows to have a single function, namely
> clk_generated_set() for enable, disable that could be used in in clock
> enable/disable APIs but also on suspend/resume function. To avoid the
> checking as, a first solution I see using another local variable that does
> what the test operator is doing here:
> 
> unsigned int x = status ? AT91_PMC_PCR_GCKEN : 0;
> 
> and then:
> 
>       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 | x,
> 
> Would you prefer having another local variable and doing like this?

Yes, you can add as many local variables with explicit names as you 
like. I think it's valuable for readability, which is always better for 
maintenance.

You can even build these variables in several steps and use them 
afteward in the regmap_update_bits() function.
I _feel_ that the compiler will anyway manage to optimize all this very 
well and produce a very similar result.

>>>                   field_prep(gck->layout->gckcss_mask, gck->parent_id) |

This previous line tells us that the parameters for this function were 
aleardy crowded, this additional change is the trigger for telling... 
"it's too much!" ;-)

>>>                   gck->layout->cmd |
>>>                   FIELD_PREP(AT91_PMC_PCR_GCKDIV_MASK, gck->gckdiv) |
>>> -               AT91_PMC_PCR_GCKEN);
>>> +               (status ? AT91_PMC_PCR_GCKEN : 0));
>>
>> Ditto
>>
>>>        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;
>>>    }
>>>    @@ -239,6 +249,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,
>>> @@ -248,6 +275,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,
>>>    };
>>>      /**
>>> @@ -314,8 +343,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;
>>> diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c
>>> index cfae2f59df66..8601b27c1ae0 100644
>>> --- 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(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
>>> @@ -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
>>> @@ -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
>>> diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c
>>> index a80427980bf7..6f1fb2eb2a8d 100644
>>> --- 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_recalc_rate(struct clk_hw *hw,
>>>        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");
>>
>> Just to be sure: I see no write to register in this function and only
>> warning print. I assume it's intended.
> 
> Yes, its intended. It is the code that was active before this patch (see
> old code in drivers/clk/at91/pmc.c). This register should be restored by
> bootloader.

Ok

> 
>>
>>> +}
>>> +
>>>    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(struct clk_hw *hw,
>>> unsigned long rate,
>>>        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(struct clk_hw
>>> *hw, unsigned long rate,
>>>            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(struct
>>> clk_hw *hw,
>>>        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(struct clk_hw
>>> *hw, unsigned long rate,
>>>    {
>>>        struct clk_master *master = to_clk_master(hw);
>>>        unsigned long flags;
>>> -    unsigned int pres;
>>> +    unsigned int pres, val;
>>> +    int ret;
>>>          pres = DIV_ROUND_CLOSEST(parent_rate, rate);
>>>        if (pres > MASTER_PRES_MAX)
>>> @@ -284,15 +356,24 @@ static int clk_master_pres_set_rate(struct clk_hw
>>> *hw, unsigned long rate,
>>>            pres = ffs(pres) - 1;
>>>          spin_lock_irqsave(master->lock, flags);
>>> +    ret = regmap_read(master->regmap, master->layout->offset, &val);
>>> +    if (ret)
>>> +        goto unlock;
>>> +
>>> +    val = (val >> master->layout->pres_shift) & MASTER_PRES_MASK;
>>> +    if (pres == val)
>>> +        goto unlock;
>>> +
>>>        regmap_update_bits(master->regmap, master->layout->offset,
>>>                   (MASTER_PRES_MASK << master->layout->pres_shift),
>>>                   (pres << master->layout->pres_shift));
>>>          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 +411,66 @@ static u8 clk_master_pres_get_parent(struct clk_hw
>>> *hw)
>>>        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);
>>> +
>>> +    pres = (val >> master->layout->pres_shift) & MASTER_PRES_MASK;
>>> +    if (pres == 3 && 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);
>>> +
>>> +    pres = (val >> master->layout->pres_shift) & MASTER_PRES_MASK;
>>> +    if (pres == 3 && 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");
>>
>> Same here: it's restored by bootloader, right?
> 
> Yes!
> 
>>
>>> +}
>>> +
>>> +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 +480,8 @@ static const struct clk_ops master_pres_ops_chg = {
>>>        .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,9 +677,9 @@ static int clk_sama7g5_master_set_parent(struct
>>> clk_hw *hw, u8 index)
>>>        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;
>>>    @@ -550,9 +688,10 @@ static int clk_sama7g5_master_enable(struct clk_hw
>>> *hw)
>>>        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 |
>>> +               (status ? PMC_MCR_EN : 0) | PMC_MCR_CSS | PMC_MCR_DIV |
>>
>> Ditto
>>
>>>                   PMC_MCR_CMD | PMC_MCR_ID_MSK,
>>> -               PMC_MCR_EN | (master->parent << PMC_MCR_CSS_SHIFT) |
>>> +               (status ? PMC_MCR_EN : 0) |
>>> +               (master->parent << PMC_MCR_CSS_SHIFT) |
>>>                   (master->div << MASTER_DIV_SHIFT) |
>>
>> Ditto
>>
>>>                   PMC_MCR_CMD | PMC_MCR_ID(master->id));
>>
>> This whole line becomes hardly readable.
> 
> It is what previously has been used except the adition of status variable.
> All the values passed to regmap_update_bits() needs to be there for proper
> functionality. The clk_sama7g5_master_set() has been added to use the same
> function, namely clk_sama7g5_master_set(), in .enable, .disable,
> .save_context, .restore_context APIs.
> 
> Would you prefer using another local variable to store the conditional
> checking of status variable:
> 
> unsigned int x = status ? PMC_MCR_EN : 0;
> 
> ?

With explicit names, yes. And as for the other above, don't hesitate to 
break down other parameters as well: like mcr_mask, mcr_value or 
something similar.


> 
> Thank you for your review,
> Claudiu
> 
>>
>>>    @@ -563,6 +702,13 @@ static int clk_sama7g5_master_enable(struct clk_hw
>>> *hw)
>>>            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 +766,23 @@ static int clk_sama7g5_master_set_rate(struct clk_hw
>>> *hw, unsigned long rate,
>>>        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 +792,8 @@ static const struct clk_ops sama7g5_master_ops = {
>>>        .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
>>> diff --git a/drivers/clk/at91/clk-peripheral.c
>>> b/drivers/clk/at91/clk-peripheral.c
>>> index 7a27ba8e0577..bf088cd06225 100644
>>> --- 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,9 +156,9 @@ static void clk_sam9x5_peripheral_autodiv(struct
>>> clk_sam9x5_peripheral *periph)
>>>        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;
>>>          if (periph->id < PERIPHERAL_ID_MIN)
>>> @@ -168,15 +169,22 @@ static int clk_sam9x5_peripheral_enable(struct
>>> clk_hw *hw)
>>>                 (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,
>>> +               (status ? AT91_PMC_PCR_EN : 0),
>>>                   field_prep(periph->layout->div_mask, periph->div) |
>>>                   periph->layout->cmd |
>>> -               AT91_PMC_PCR_EN);
>>> +               (status ? AT91_PMC_PCR_EN : 0));
>>
>> Ditto
>>
>>>        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_rate(struct
>>> clk_hw *hw,
>>>        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_peripheral_ops = {
>>>        .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_peripheral_chg_ops
>>> = {
>>>        .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(struct regmap
>>> *regmap, spinlock_t *lock,
>>>            hw = ERR_PTR(ret);
>>>        } else {
>>>            clk_sam9x5_peripheral_autodiv(periph);
>>> -        pmc_register_id(id);
>>>        }
>>>          return hw;
>>> diff --git a/drivers/clk/at91/clk-pll.c b/drivers/clk/at91/clk-pll.c
>>> index 6ed986d3eee0..31964622c658 100644
>>> --- 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,40 @@ static int clk_pll_set_rate(struct clk_hw *hw,
>>> unsigned long rate,
>>>        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;
>>> +    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);
>>> +
>>> +    if (pll->pms.rate != calc_rate ||
>>> +        pll->pms.status != clk_pll_ready(pll->regmap, PLL_REG(pll->id)) ||
>>> +        (((pllr >> PLL_COUNT_SHIFT) & PLL_MAX_COUNT) != PLL_MAX_COUNT) ||
>>> +        (out && ((pllr >> PLL_OUT_SHIFT) & 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 +302,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
>>> diff --git a/drivers/clk/at91/clk-programmable.c
>>> b/drivers/clk/at91/clk-programmable.c
>>> index fcf8f6a1c2c6..6c4b259d31d3 100644
>>> --- 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(struct clk_hw
>>> *hw, unsigned long rate,
>>>        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 regmap *regmap,
>>>        if (ret) {
>>>            kfree(prog);
>>>            hw = ERR_PTR(ret);
>>> -    } else {
>>> -        pmc_register_pck(id);
>>>        }
>>>          return hw;
>>> diff --git a/drivers/clk/at91/clk-sam9x60-pll.c
>>> b/drivers/clk/at91/clk-sam9x60-pll.c
>>> index 34e3ab13741a..7020d3bf6e13 100644
>>> --- 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_recalc_rate(struct clk_hw *hw,
>>>            ((u64)parent_rate * frac->frac >> 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 @@ static int sam9x60_frac_pll_prepare(struct clk_hw *hw)
>>>        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 @@ static int sam9x60_frac_pll_set_rate_chg(struct
>>> clk_hw *hw, unsigned long rate,
>>>        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_pll_ops = {
>>>        .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_pll_ops_chg = {
>>>        .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 @@ static int sam9x60_div_pll_prepare(struct clk_hw *hw)
>>>        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 @@ static int sam9x60_div_pll_set_rate_chg(struct
>>> clk_hw *hw, unsigned long rate,
>>>        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_pll_ops = {
>>>        .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_pll_ops_chg = {
>>>        .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
>>> diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c
>>> index f83ec0de86c3..80720fd1a9cf 100644
>>> --- 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 clk_hw *hw)
>>>        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
>>> diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c
>>> index 31d5c45e30d7..b0696a928aa9 100644
>>> --- 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(struct
>>> clk_hw *hw, unsigned long rate,
>>>        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)
>>> diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c
>>> index df9f3fc3b6a6..2b102ee2a7d3 100644
>>> --- 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_rate(struct
>>> clk_hw *hw,
>>>        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,35 @@ static int clk_utmi_sama7g5_is_prepared(struct
>>> clk_hw *hw)
>>>        return 0;
>>>    }
>>>    +static unsigned long clk_utmi_sama7g5_recalc_rate(struct clk_hw *hw,
>>> +                          unsigned long parent_rate)
>>> +{
>>> +    return 480000000;
>>> +}
>>> +
>>> +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,
>>> +    .recalc_rate = clk_utmi_sama7g5_recalc_rate,
>>> +    .save_context = clk_utmi_sama7g5_save_context,
>>> +    .restore_context = clk_utmi_sama7g5_restore_context,
>>>    };
>>>      struct clk_hw * __init
>>> diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
>>> index 20ee9dccee78..77b57c9f5dcb 100644
>>> --- a/drivers/clk/at91/pmc.c
>>> +++ b/drivers/clk/at91/pmc.c
>>> @@ -3,6 +3,7 @@
>>>     *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
>>>     */
>>>    +#include <linux/clk.h>
>>>    #include <linux/clk-provider.h>
>>>    #include <linux/clkdev.h>
>>>    #include <linux/clk/at91_pmc.h>
>>> @@ -14,8 +15,6 @@
>>>      #include <asm/proc-fns.h>
>>>    -#include <dt-bindings/clock/at91.h>
>>> -
>>>    #include "pmc.h"
>>>      #define PMC_MAX_IDS 128
>>> @@ -111,147 +110,19 @@ struct pmc_data *pmc_data_allocate(unsigned int
>>> ncore, unsigned int nsystem,
>>>    }
>>>      #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)
>>> -{
>>> -    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;
>>> -    }
>>> -}
>>> -
>>> -/*
>>> - * 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)
>>> -{
>>> -    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)
>>> +static int at91_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;
>>> +    return clk_save_context();
>>>    }
>>>    -static bool pmc_ready(unsigned int mask)
>>> +static void at91_pmc_resume(void)
>>>    {
>>> -    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();
>>>    }
>>
>> I like how it simplify this part! Good.
>>
>>>      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[] = {
>>> @@ -265,12 +136,8 @@ static int __init pmc_register_ops(void)
>>>          np = of_find_matching_node(NULL, sama5d2_pmc_dt_ids);
>>>        if (!np)
>>> -        return -ENODEV;
>>> -
>>> -    pmcreg = device_node_to_regmap(np);
>>> +        return 0;
>>>        of_node_put(np);
>>> -    if (IS_ERR(pmcreg))
>>> -        return PTR_ERR(pmcreg);
>>>          register_syscore_ops(&pmc_syscore_ops);
>>>    diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
>>> index a49076c804a9..86580ebd9ad9 100644
>>> --- a/drivers/clk/at91/pmc.h
>>> +++ b/drivers/clk/at91/pmc.h
>>> @@ -13,6 +13,8 @@
>>>    #include <linux/regmap.h>
>>>    #include <linux/spinlock.h>
>>>    +#include <dt-bindings/clock/at91.h>
>>> +
>>>    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
>>> + * @status: clock status (enabled or disabled)
>>> + * @parent: clock parent index
>>> + * @parent_rate: clock parent rate
>>> + * @rate: clock rate
>>> + */
>>> +struct at91_clk_pms {
>>> +    unsigned int status;
>>> +    unsigned int parent;
>>> +    unsigned long parent_rate;
>>> +    unsigned long rate;
>>> +};
>>> +
>>>    #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_ */
>>>
>>
>>
> 


-- 
Nicolas Ferre

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2021-03-31 11:38 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-24  9:43 [RESEND PATCH 0/5] clk: at91: updates for power management and dvfs Claudiu Beznea
2021-03-24  9:43 ` [RESEND PATCH 1/5] clk: at91: re-factor clocks suspend/resume Claudiu Beznea
2021-03-30 17:14   ` Nicolas Ferre
2021-03-31  8:47     ` Claudiu.Beznea
2021-03-31 11:37       ` Nicolas Ferre
2021-03-24  9:43 ` [RESEND PATCH 2/5] clk: at91: pmc: execute suspend/resume only for backup mode Claudiu Beznea
2021-03-24  9:43 ` [RESEND PATCH 3/5] clk: at91: sama7g5: add securam's peripheral clock Claudiu Beznea
2021-03-30 17:27   ` Nicolas Ferre
2021-03-24  9:43 ` [RESEND PATCH 4/5] clk: at91: clk-master: add register definition for sama7g5's master clock Claudiu Beznea
2021-03-30 17:23   ` Nicolas Ferre
2021-03-24  9:43 ` [RESEND PATCH 5/5] clk: at91: clk-master: update for dvfs Claudiu Beznea

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).