diff options
-rw-r--r-- | drivers/cpufreq/armada-37xx-cpufreq.c | 67 |
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 | ||
61 | struct 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 | |||
69 | static struct armada37xx_cpufreq_state *armada37xx_cpufreq_state; | ||
70 | |||
59 | struct armada_37xx_dvfs { | 71 | struct 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 | ||
139 | static void __init armada37xx_cpufreq_disable_dvfs(struct regmap *base) | 151 | static 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 | ||
177 | static 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 | |||
190 | static 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 | |||
165 | static int __init armada37xx_cpufreq_driver_init(void) | 212 | static 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 */ |