summaryrefslogtreecommitdiffstats
path: root/drivers/cpufreq
diff options
context:
space:
mode:
authorMiquel Raynal <miquel.raynal@bootlin.com>2018-04-26 06:12:17 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2018-05-10 05:43:59 -0400
commit9c28d6df7534e1b64d485fde02ebe62375ea9bcc (patch)
treead8861df6b4b97b62318546b433329e4e69979d9 /drivers/cpufreq
parentcfd84631d976777e4b598d158e968c7bd55cf70a (diff)
cpufreq: add suspend/resume support in Armada 37xx DVFS driver
Add suspend/resume hooks in Armada 37xx DVFS driver to handle S2RAM operations. As there is currently no 'driver' structure, create one to store both the regmap and the register values during suspend operation. Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Tested-by: Gregory CLEMENT <gregory.clement@bootlin.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/cpufreq')
-rw-r--r--drivers/cpufreq/armada-37xx-cpufreq.c67
1 files changed, 65 insertions, 2 deletions
diff --git a/drivers/cpufreq/armada-37xx-cpufreq.c b/drivers/cpufreq/armada-37xx-cpufreq.c
index 1d5db6f6ace9..739da90ff3f6 100644
--- a/drivers/cpufreq/armada-37xx-cpufreq.c
+++ b/drivers/cpufreq/armada-37xx-cpufreq.c
@@ -23,6 +23,8 @@
23#include <linux/regmap.h> 23#include <linux/regmap.h>
24#include <linux/slab.h> 24#include <linux/slab.h>
25 25
26#include "cpufreq-dt.h"
27
26/* Power management in North Bridge register set */ 28/* Power management in North Bridge register set */
27#define ARMADA_37XX_NB_L0L1 0x18 29#define ARMADA_37XX_NB_L0L1 0x18
28#define ARMADA_37XX_NB_L2L3 0x1C 30#define ARMADA_37XX_NB_L2L3 0x1C
@@ -56,6 +58,16 @@
56 */ 58 */
57#define LOAD_LEVEL_NR 4 59#define LOAD_LEVEL_NR 4
58 60
61struct armada37xx_cpufreq_state {
62 struct regmap *regmap;
63 u32 nb_l0l1;
64 u32 nb_l2l3;
65 u32 nb_dyn_mod;
66 u32 nb_cpu_load;
67};
68
69static struct armada37xx_cpufreq_state *armada37xx_cpufreq_state;
70
59struct armada_37xx_dvfs { 71struct armada_37xx_dvfs {
60 u32 cpu_freq_max; 72 u32 cpu_freq_max;
61 u8 divider[LOAD_LEVEL_NR]; 73 u8 divider[LOAD_LEVEL_NR];
@@ -136,7 +148,7 @@ static void __init armada37xx_cpufreq_dvfs_setup(struct regmap *base,
136 clk_set_parent(clk, parent); 148 clk_set_parent(clk, parent);
137} 149}
138 150
139static void __init armada37xx_cpufreq_disable_dvfs(struct regmap *base) 151static void armada37xx_cpufreq_disable_dvfs(struct regmap *base)
140{ 152{
141 unsigned int reg = ARMADA_37XX_NB_DYN_MOD, 153 unsigned int reg = ARMADA_37XX_NB_DYN_MOD,
142 mask = ARMADA_37XX_NB_DFS_EN; 154 mask = ARMADA_37XX_NB_DFS_EN;
@@ -162,8 +174,44 @@ static void __init armada37xx_cpufreq_enable_dvfs(struct regmap *base)
162 regmap_update_bits(base, reg, mask, mask); 174 regmap_update_bits(base, reg, mask, mask);
163} 175}
164 176
177static int armada37xx_cpufreq_suspend(struct cpufreq_policy *policy)
178{
179 struct armada37xx_cpufreq_state *state = armada37xx_cpufreq_state;
180
181 regmap_read(state->regmap, ARMADA_37XX_NB_L0L1, &state->nb_l0l1);
182 regmap_read(state->regmap, ARMADA_37XX_NB_L2L3, &state->nb_l2l3);
183 regmap_read(state->regmap, ARMADA_37XX_NB_CPU_LOAD,
184 &state->nb_cpu_load);
185 regmap_read(state->regmap, ARMADA_37XX_NB_DYN_MOD, &state->nb_dyn_mod);
186
187 return 0;
188}
189
190static int armada37xx_cpufreq_resume(struct cpufreq_policy *policy)
191{
192 struct armada37xx_cpufreq_state *state = armada37xx_cpufreq_state;
193
194 /* Ensure DVFS is disabled otherwise the following registers are RO */
195 armada37xx_cpufreq_disable_dvfs(state->regmap);
196
197 regmap_write(state->regmap, ARMADA_37XX_NB_L0L1, state->nb_l0l1);
198 regmap_write(state->regmap, ARMADA_37XX_NB_L2L3, state->nb_l2l3);
199 regmap_write(state->regmap, ARMADA_37XX_NB_CPU_LOAD,
200 state->nb_cpu_load);
201
202 /*
203 * NB_DYN_MOD register is the one that actually enable back DVFS if it
204 * was enabled before the suspend operation. This must be done last
205 * otherwise other registers are not writable.
206 */
207 regmap_write(state->regmap, ARMADA_37XX_NB_DYN_MOD, state->nb_dyn_mod);
208
209 return 0;
210}
211
165static int __init armada37xx_cpufreq_driver_init(void) 212static int __init armada37xx_cpufreq_driver_init(void)
166{ 213{
214 struct cpufreq_dt_platform_data pdata;
167 struct armada_37xx_dvfs *dvfs; 215 struct armada_37xx_dvfs *dvfs;
168 struct platform_device *pdev; 216 struct platform_device *pdev;
169 unsigned long freq; 217 unsigned long freq;
@@ -213,6 +261,15 @@ static int __init armada37xx_cpufreq_driver_init(void)
213 return -EINVAL; 261 return -EINVAL;
214 } 262 }
215 263
264 armada37xx_cpufreq_state = kmalloc(sizeof(*armada37xx_cpufreq_state),
265 GFP_KERNEL);
266 if (!armada37xx_cpufreq_state) {
267 clk_put(clk);
268 return -ENOMEM;
269 }
270
271 armada37xx_cpufreq_state->regmap = nb_pm_base;
272
216 armada37xx_cpufreq_dvfs_setup(nb_pm_base, clk, dvfs->divider); 273 armada37xx_cpufreq_dvfs_setup(nb_pm_base, clk, dvfs->divider);
217 clk_put(clk); 274 clk_put(clk);
218 275
@@ -228,7 +285,11 @@ static int __init armada37xx_cpufreq_driver_init(void)
228 /* Now that everything is setup, enable the DVFS at hardware level */ 285 /* Now that everything is setup, enable the DVFS at hardware level */
229 armada37xx_cpufreq_enable_dvfs(nb_pm_base); 286 armada37xx_cpufreq_enable_dvfs(nb_pm_base);
230 287
231 pdev = platform_device_register_simple("cpufreq-dt", -1, NULL, 0); 288 pdata.suspend = armada37xx_cpufreq_suspend;
289 pdata.resume = armada37xx_cpufreq_resume;
290
291 pdev = platform_device_register_data(NULL, "cpufreq-dt", -1, &pdata,
292 sizeof(pdata));
232 ret = PTR_ERR_OR_ZERO(pdev); 293 ret = PTR_ERR_OR_ZERO(pdev);
233 if (ret) 294 if (ret)
234 goto disable_dvfs; 295 goto disable_dvfs;
@@ -244,6 +305,8 @@ remove_opp:
244 dev_pm_opp_remove(cpu_dev, freq); 305 dev_pm_opp_remove(cpu_dev, freq);
245 } 306 }
246 307
308 kfree(armada37xx_cpufreq_state);
309
247 return ret; 310 return ret;
248} 311}
249/* late_initcall, to guarantee the driver is loaded after A37xx clock driver */ 312/* late_initcall, to guarantee the driver is loaded after A37xx clock driver */