aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 */