aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRomain Izard <romain.izard.pro@gmail.com>2017-12-11 11:55:35 -0500
committerStephen Boyd <sboyd@codeaurora.org>2017-12-21 19:34:06 -0500
commit13967bea0bdb194b8674b4102fcdd383a8a18baa (patch)
tree6ef38889a8380a1cd0011ceae2416775f47ebcc6
parent3c6fad2593d75a1674c5c2b19c78552c48ef46b5 (diff)
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: Romain Izard <romain.izard.pro@gmail.com> Acked-by: Nicolas Ferre <nicolas.ferre@microchip.com> Acked-by: Alexandre Belloni <alexandre.belloni@free-electrons.com> Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
-rw-r--r--drivers/clk/at91/clk-programmable.c2
-rw-r--r--drivers/clk/at91/pmc.c35
-rw-r--r--drivers/clk/at91/pmc.h2
3 files changed, 39 insertions, 0 deletions
diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c
index 85a449cf61e3..0e6aab1252fc 100644
--- a/drivers/clk/at91/clk-programmable.c
+++ b/drivers/clk/at91/clk-programmable.c
@@ -204,6 +204,8 @@ at91_clk_register_programmable(struct regmap *regmap,
204 if (ret) { 204 if (ret) {
205 kfree(prog); 205 kfree(prog);
206 hw = ERR_PTR(ret); 206 hw = ERR_PTR(ret);
207 } else {
208 pmc_register_pck(id);
207 } 209 }
208 210
209 return hw; 211 return hw;
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 07dc2861ad3f..1fa27f4ea538 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -22,6 +22,7 @@
22#include "pmc.h" 22#include "pmc.h"
23 23
24#define PMC_MAX_IDS 128 24#define PMC_MAX_IDS 128
25#define PMC_MAX_PCKS 8
25 26
26int of_at91_get_clk_range(struct device_node *np, const char *propname, 27int of_at91_get_clk_range(struct device_node *np, const char *propname,
27 struct clk_range *range) 28 struct clk_range *range)
@@ -50,6 +51,7 @@ EXPORT_SYMBOL_GPL(of_at91_get_clk_range);
50static struct regmap *pmcreg; 51static struct regmap *pmcreg;
51 52
52static u8 registered_ids[PMC_MAX_IDS]; 53static u8 registered_ids[PMC_MAX_IDS];
54static u8 registered_pcks[PMC_MAX_PCKS];
53 55
54static struct 56static struct
55{ 57{
@@ -66,8 +68,13 @@ static struct
66 u32 pcr[PMC_MAX_IDS]; 68 u32 pcr[PMC_MAX_IDS];
67 u32 audio_pll0; 69 u32 audio_pll0;
68 u32 audio_pll1; 70 u32 audio_pll1;
71 u32 pckr[PMC_MAX_PCKS];
69} pmc_cache; 72} pmc_cache;
70 73
74/*
75 * As Peripheral ID 0 is invalid on AT91 chips, the identifier is stored
76 * without alteration in the table, and 0 is for unused clocks.
77 */
71void pmc_register_id(u8 id) 78void pmc_register_id(u8 id)
72{ 79{
73 int i; 80 int i;
@@ -82,9 +89,28 @@ void pmc_register_id(u8 id)
82 } 89 }
83} 90}
84 91
92/*
93 * As Programmable Clock 0 is valid on AT91 chips, there is an offset
94 * of 1 between the stored value and the real clock ID.
95 */
96void pmc_register_pck(u8 pck)
97{
98 int i;
99
100 for (i = 0; i < PMC_MAX_PCKS; i++) {
101 if (registered_pcks[i] == 0) {
102 registered_pcks[i] = pck + 1;
103 break;
104 }
105 if (registered_pcks[i] == (pck + 1))
106 break;
107 }
108}
109
85static int pmc_suspend(void) 110static int pmc_suspend(void)
86{ 111{
87 int i; 112 int i;
113 u8 num;
88 114
89 regmap_read(pmcreg, AT91_PMC_SCSR, &pmc_cache.scsr); 115 regmap_read(pmcreg, AT91_PMC_SCSR, &pmc_cache.scsr);
90 regmap_read(pmcreg, AT91_PMC_PCSR, &pmc_cache.pcsr0); 116 regmap_read(pmcreg, AT91_PMC_PCSR, &pmc_cache.pcsr0);
@@ -103,6 +129,10 @@ static int pmc_suspend(void)
103 regmap_read(pmcreg, AT91_PMC_PCR, 129 regmap_read(pmcreg, AT91_PMC_PCR,
104 &pmc_cache.pcr[registered_ids[i]]); 130 &pmc_cache.pcr[registered_ids[i]]);
105 } 131 }
132 for (i = 0; registered_pcks[i]; i++) {
133 num = registered_pcks[i] - 1;
134 regmap_read(pmcreg, AT91_PMC_PCKR(num), &pmc_cache.pckr[num]);
135 }
106 136
107 return 0; 137 return 0;
108} 138}
@@ -119,6 +149,7 @@ static bool pmc_ready(unsigned int mask)
119static void pmc_resume(void) 149static void pmc_resume(void)
120{ 150{
121 int i; 151 int i;
152 u8 num;
122 u32 tmp; 153 u32 tmp;
123 u32 mask = AT91_PMC_MCKRDY | AT91_PMC_LOCKA; 154 u32 mask = AT91_PMC_MCKRDY | AT91_PMC_LOCKA;
124 155
@@ -143,6 +174,10 @@ static void pmc_resume(void)
143 pmc_cache.pcr[registered_ids[i]] | 174 pmc_cache.pcr[registered_ids[i]] |
144 AT91_PMC_PCR_CMD); 175 AT91_PMC_PCR_CMD);
145 } 176 }
177 for (i = 0; registered_pcks[i]; i++) {
178 num = registered_pcks[i] - 1;
179 regmap_write(pmcreg, AT91_PMC_PCKR(num), pmc_cache.pckr[num]);
180 }
146 181
147 if (pmc_cache.uckr & AT91_PMC_UPLLEN) 182 if (pmc_cache.uckr & AT91_PMC_UPLLEN)
148 mask |= AT91_PMC_LOCKU; 183 mask |= AT91_PMC_LOCKU;
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 858e8ef7e8db..d22b1fa9ecdc 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -31,8 +31,10 @@ int of_at91_get_clk_range(struct device_node *np, const char *propname,
31 31
32#ifdef CONFIG_PM 32#ifdef CONFIG_PM
33void pmc_register_id(u8 id); 33void pmc_register_id(u8 id);
34void pmc_register_pck(u8 pck);
34#else 35#else
35static inline void pmc_register_id(u8 id) {} 36static inline void pmc_register_id(u8 id) {}
37static inline void pmc_register_pck(u8 pck) {}
36#endif 38#endif
37 39
38#endif /* __PMC_H_ */ 40#endif /* __PMC_H_ */