Commit 13967bea authored by Romain Izard's avatar Romain Izard Committed by Stephen Boyd

clk: at91: pmc: Support backup for programmable clocks

When an AT91 programmable clock is declared in the device tree, register
it into the Power Management Controller driver. On entering suspend mode,
the driver saves and restores the Programmable Clock registers to support
the backup mode for these clocks.
Signed-off-by: default avatarRomain Izard <romain.izard.pro@gmail.com>
Acked-by: default avatarNicolas Ferre <nicolas.ferre@microchip.com>
Acked-by: default avatarAlexandre Belloni <alexandre.belloni@free-electrons.com>
Signed-off-by: default avatarStephen Boyd <sboyd@codeaurora.org>
parent 3c6fad25
...@@ -204,6 +204,8 @@ at91_clk_register_programmable(struct regmap *regmap, ...@@ -204,6 +204,8 @@ at91_clk_register_programmable(struct regmap *regmap,
if (ret) { if (ret) {
kfree(prog); kfree(prog);
hw = ERR_PTR(ret); hw = ERR_PTR(ret);
} else {
pmc_register_pck(id);
} }
return hw; return hw;
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "pmc.h" #include "pmc.h"
#define PMC_MAX_IDS 128 #define PMC_MAX_IDS 128
#define PMC_MAX_PCKS 8
int of_at91_get_clk_range(struct device_node *np, const char *propname, int of_at91_get_clk_range(struct device_node *np, const char *propname,
struct clk_range *range) struct clk_range *range)
...@@ -50,6 +51,7 @@ EXPORT_SYMBOL_GPL(of_at91_get_clk_range); ...@@ -50,6 +51,7 @@ EXPORT_SYMBOL_GPL(of_at91_get_clk_range);
static struct regmap *pmcreg; static struct regmap *pmcreg;
static u8 registered_ids[PMC_MAX_IDS]; static u8 registered_ids[PMC_MAX_IDS];
static u8 registered_pcks[PMC_MAX_PCKS];
static struct static struct
{ {
...@@ -66,8 +68,13 @@ static struct ...@@ -66,8 +68,13 @@ static struct
u32 pcr[PMC_MAX_IDS]; u32 pcr[PMC_MAX_IDS];
u32 audio_pll0; u32 audio_pll0;
u32 audio_pll1; u32 audio_pll1;
u32 pckr[PMC_MAX_PCKS];
} pmc_cache; } 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) void pmc_register_id(u8 id)
{ {
int i; int i;
...@@ -82,9 +89,28 @@ void pmc_register_id(u8 id) ...@@ -82,9 +89,28 @@ void pmc_register_id(u8 id)
} }
} }
/*
* 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 pmc_suspend(void)
{ {
int i; int i;
u8 num;
regmap_read(pmcreg, AT91_PMC_SCSR, &pmc_cache.scsr); regmap_read(pmcreg, AT91_PMC_SCSR, &pmc_cache.scsr);
regmap_read(pmcreg, AT91_PMC_PCSR, &pmc_cache.pcsr0); regmap_read(pmcreg, AT91_PMC_PCSR, &pmc_cache.pcsr0);
...@@ -103,6 +129,10 @@ static int pmc_suspend(void) ...@@ -103,6 +129,10 @@ static int pmc_suspend(void)
regmap_read(pmcreg, AT91_PMC_PCR, regmap_read(pmcreg, AT91_PMC_PCR,
&pmc_cache.pcr[registered_ids[i]]); &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 0;
} }
...@@ -119,6 +149,7 @@ static bool pmc_ready(unsigned int mask) ...@@ -119,6 +149,7 @@ static bool pmc_ready(unsigned int mask)
static void pmc_resume(void) static void pmc_resume(void)
{ {
int i; int i;
u8 num;
u32 tmp; u32 tmp;
u32 mask = AT91_PMC_MCKRDY | AT91_PMC_LOCKA; u32 mask = AT91_PMC_MCKRDY | AT91_PMC_LOCKA;
...@@ -143,6 +174,10 @@ static void pmc_resume(void) ...@@ -143,6 +174,10 @@ static void pmc_resume(void)
pmc_cache.pcr[registered_ids[i]] | pmc_cache.pcr[registered_ids[i]] |
AT91_PMC_PCR_CMD); 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) if (pmc_cache.uckr & AT91_PMC_UPLLEN)
mask |= AT91_PMC_LOCKU; mask |= AT91_PMC_LOCKU;
......
...@@ -31,8 +31,10 @@ int of_at91_get_clk_range(struct device_node *np, const char *propname, ...@@ -31,8 +31,10 @@ int of_at91_get_clk_range(struct device_node *np, const char *propname,
#ifdef CONFIG_PM #ifdef CONFIG_PM
void pmc_register_id(u8 id); void pmc_register_id(u8 id);
void pmc_register_pck(u8 pck);
#else #else
static inline void pmc_register_id(u8 id) {} static inline void pmc_register_id(u8 id) {}
static inline void pmc_register_pck(u8 pck) {}
#endif #endif
#endif /* __PMC_H_ */ #endif /* __PMC_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment