Loading drivers/cpufreq/armada-37xx-cpufreq.c +87 −13 Original line number Diff line number Diff line Loading @@ -23,6 +23,8 @@ #include <linux/regmap.h> #include <linux/slab.h> #include "cpufreq-dt.h" /* Power management in North Bridge register set */ #define ARMADA_37XX_NB_L0L1 0x18 #define ARMADA_37XX_NB_L2L3 0x1C Loading Loading @@ -56,6 +58,16 @@ */ #define LOAD_LEVEL_NR 4 struct armada37xx_cpufreq_state { struct regmap *regmap; u32 nb_l0l1; u32 nb_l2l3; u32 nb_dyn_mod; u32 nb_cpu_load; }; static struct armada37xx_cpufreq_state *armada37xx_cpufreq_state; struct armada_37xx_dvfs { u32 cpu_freq_max; u8 divider[LOAD_LEVEL_NR]; Loading Loading @@ -136,7 +148,7 @@ static void __init armada37xx_cpufreq_dvfs_setup(struct regmap *base, clk_set_parent(clk, parent); } static void __init armada37xx_cpufreq_disable_dvfs(struct regmap *base) static void armada37xx_cpufreq_disable_dvfs(struct regmap *base) { unsigned int reg = ARMADA_37XX_NB_DYN_MOD, mask = ARMADA_37XX_NB_DFS_EN; Loading @@ -162,10 +174,47 @@ static void __init armada37xx_cpufreq_enable_dvfs(struct regmap *base) regmap_update_bits(base, reg, mask, mask); } static int armada37xx_cpufreq_suspend(struct cpufreq_policy *policy) { struct armada37xx_cpufreq_state *state = armada37xx_cpufreq_state; regmap_read(state->regmap, ARMADA_37XX_NB_L0L1, &state->nb_l0l1); regmap_read(state->regmap, ARMADA_37XX_NB_L2L3, &state->nb_l2l3); regmap_read(state->regmap, ARMADA_37XX_NB_CPU_LOAD, &state->nb_cpu_load); regmap_read(state->regmap, ARMADA_37XX_NB_DYN_MOD, &state->nb_dyn_mod); return 0; } static int armada37xx_cpufreq_resume(struct cpufreq_policy *policy) { struct armada37xx_cpufreq_state *state = armada37xx_cpufreq_state; /* Ensure DVFS is disabled otherwise the following registers are RO */ armada37xx_cpufreq_disable_dvfs(state->regmap); regmap_write(state->regmap, ARMADA_37XX_NB_L0L1, state->nb_l0l1); regmap_write(state->regmap, ARMADA_37XX_NB_L2L3, state->nb_l2l3); regmap_write(state->regmap, ARMADA_37XX_NB_CPU_LOAD, state->nb_cpu_load); /* * NB_DYN_MOD register is the one that actually enable back DVFS if it * was enabled before the suspend operation. This must be done last * otherwise other registers are not writable. */ regmap_write(state->regmap, ARMADA_37XX_NB_DYN_MOD, state->nb_dyn_mod); return 0; } static int __init armada37xx_cpufreq_driver_init(void) { struct cpufreq_dt_platform_data pdata; struct armada_37xx_dvfs *dvfs; struct platform_device *pdev; unsigned long freq; unsigned int cur_frequency; struct regmap *nb_pm_base; struct device *cpu_dev; Loading Loading @@ -207,33 +256,58 @@ static int __init armada37xx_cpufreq_driver_init(void) } dvfs = armada_37xx_cpu_freq_info_get(cur_frequency); if (!dvfs) if (!dvfs) { clk_put(clk); return -EINVAL; } armada37xx_cpufreq_state = kmalloc(sizeof(*armada37xx_cpufreq_state), GFP_KERNEL); if (!armada37xx_cpufreq_state) { clk_put(clk); return -ENOMEM; } armada37xx_cpufreq_state->regmap = nb_pm_base; armada37xx_cpufreq_dvfs_setup(nb_pm_base, clk, dvfs->divider); clk_put(clk); for (load_lvl = ARMADA_37XX_DVFS_LOAD_0; load_lvl < LOAD_LEVEL_NR; load_lvl++) { unsigned long freq = cur_frequency / dvfs->divider[load_lvl]; freq = cur_frequency / dvfs->divider[load_lvl]; ret = dev_pm_opp_add(cpu_dev, freq, 0); if (ret) { if (ret) goto remove_opp; } /* Now that everything is setup, enable the DVFS at hardware level */ armada37xx_cpufreq_enable_dvfs(nb_pm_base); pdata.suspend = armada37xx_cpufreq_suspend; pdata.resume = armada37xx_cpufreq_resume; pdev = platform_device_register_data(NULL, "cpufreq-dt", -1, &pdata, sizeof(pdata)); ret = PTR_ERR_OR_ZERO(pdev); if (ret) goto disable_dvfs; return 0; disable_dvfs: armada37xx_cpufreq_disable_dvfs(nb_pm_base); remove_opp: /* clean-up the already added opp before leaving */ while (load_lvl-- > ARMADA_37XX_DVFS_LOAD_0) { freq = cur_frequency / dvfs->divider[load_lvl]; dev_pm_opp_remove(cpu_dev, freq); } return ret; } } /* Now that everything is setup, enable the DVFS at hardware level */ armada37xx_cpufreq_enable_dvfs(nb_pm_base); kfree(armada37xx_cpufreq_state); pdev = platform_device_register_simple("cpufreq-dt", -1, NULL, 0); return PTR_ERR_OR_ZERO(pdev); return ret; } /* late_initcall, to guarantee the driver is loaded after A37xx clock driver */ late_initcall(armada37xx_cpufreq_driver_init); Loading drivers/cpufreq/cpufreq-dt-platdev.c +0 −2 Original line number Diff line number Diff line Loading @@ -66,8 +66,6 @@ static const struct of_device_id whitelist[] __initconst = { { .compatible = "renesas,r8a7792", }, { .compatible = "renesas,r8a7793", }, { .compatible = "renesas,r8a7794", }, { .compatible = "renesas,r8a7795", }, { .compatible = "renesas,r8a7796", }, { .compatible = "renesas,sh73a0", }, { .compatible = "rockchip,rk2928", }, Loading drivers/cpufreq/cpufreq-dt.c +8 −2 Original line number Diff line number Diff line Loading @@ -346,9 +346,15 @@ static int dt_cpufreq_probe(struct platform_device *pdev) if (ret) return ret; if (data && data->have_governor_per_policy) if (data) { if (data->have_governor_per_policy) dt_cpufreq_driver.flags |= CPUFREQ_HAVE_GOVERNOR_PER_POLICY; dt_cpufreq_driver.resume = data->resume; if (data->suspend) dt_cpufreq_driver.suspend = data->suspend; } ret = cpufreq_register_driver(&dt_cpufreq_driver); if (ret) dev_err(&pdev->dev, "failed register driver: %d\n", ret); Loading drivers/cpufreq/cpufreq-dt.h +5 −0 Original line number Diff line number Diff line Loading @@ -12,8 +12,13 @@ #include <linux/types.h> struct cpufreq_policy; struct cpufreq_dt_platform_data { bool have_governor_per_policy; int (*suspend)(struct cpufreq_policy *policy); int (*resume)(struct cpufreq_policy *policy); }; #endif /* __CPUFREQ_DT_H__ */ drivers/cpufreq/cpufreq.c +32 −31 Original line number Diff line number Diff line Loading @@ -300,8 +300,19 @@ static void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) #endif } static void __cpufreq_notify_transition(struct cpufreq_policy *policy, struct cpufreq_freqs *freqs, unsigned int state) /** * cpufreq_notify_transition - Notify frequency transition and adjust_jiffies. * @policy: cpufreq policy to enable fast frequency switching for. * @freqs: contain details of the frequency update. * @state: set to CPUFREQ_PRECHANGE or CPUFREQ_POSTCHANGE. * * This function calls the transition notifiers and the "adjust_jiffies" * function. It is called twice on all CPU frequency changes that have * external effects. */ static void cpufreq_notify_transition(struct cpufreq_policy *policy, struct cpufreq_freqs *freqs, unsigned int state) { BUG_ON(irqs_disabled()); Loading @@ -313,52 +324,42 @@ static void __cpufreq_notify_transition(struct cpufreq_policy *policy, state, freqs->new); switch (state) { case CPUFREQ_PRECHANGE: /* detect if the driver reported a value as "old frequency" /* * Detect if the driver reported a value as "old frequency" * which is not equal to what the cpufreq core thinks is * "old frequency". */ if (!(cpufreq_driver->flags & CPUFREQ_CONST_LOOPS)) { if ((policy) && (policy->cpu == freqs->cpu) && (policy->cur) && (policy->cur != freqs->old)) { if (policy->cur && (policy->cur != freqs->old)) { pr_debug("Warning: CPU frequency is %u, cpufreq assumed %u kHz\n", freqs->old, policy->cur); freqs->old = policy->cur; } } for_each_cpu(freqs->cpu, policy->cpus) { srcu_notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_PRECHANGE, freqs); } adjust_jiffies(CPUFREQ_PRECHANGE, freqs); break; case CPUFREQ_POSTCHANGE: adjust_jiffies(CPUFREQ_POSTCHANGE, freqs); pr_debug("FREQ: %lu - CPU: %lu\n", (unsigned long)freqs->new, (unsigned long)freqs->cpu); pr_debug("FREQ: %u - CPUs: %*pbl\n", freqs->new, cpumask_pr_args(policy->cpus)); for_each_cpu(freqs->cpu, policy->cpus) { trace_cpu_frequency(freqs->new, freqs->cpu); cpufreq_stats_record_transition(policy, freqs->new); srcu_notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_POSTCHANGE, freqs); if (likely(policy) && likely(policy->cpu == freqs->cpu)) policy->cur = freqs->new; break; } } /** * cpufreq_notify_transition - call notifier chain and adjust_jiffies * on frequency transition. * * This function calls the transition notifiers and the "adjust_jiffies" * function. It is called twice on all CPU frequency changes that have * external effects. */ static void cpufreq_notify_transition(struct cpufreq_policy *policy, struct cpufreq_freqs *freqs, unsigned int state) { for_each_cpu(freqs->cpu, policy->cpus) __cpufreq_notify_transition(policy, freqs, state); cpufreq_stats_record_transition(policy, freqs->new); policy->cur = freqs->new; } } /* Do post notifications when there are chances that transition has failed */ Loading Loading
drivers/cpufreq/armada-37xx-cpufreq.c +87 −13 Original line number Diff line number Diff line Loading @@ -23,6 +23,8 @@ #include <linux/regmap.h> #include <linux/slab.h> #include "cpufreq-dt.h" /* Power management in North Bridge register set */ #define ARMADA_37XX_NB_L0L1 0x18 #define ARMADA_37XX_NB_L2L3 0x1C Loading Loading @@ -56,6 +58,16 @@ */ #define LOAD_LEVEL_NR 4 struct armada37xx_cpufreq_state { struct regmap *regmap; u32 nb_l0l1; u32 nb_l2l3; u32 nb_dyn_mod; u32 nb_cpu_load; }; static struct armada37xx_cpufreq_state *armada37xx_cpufreq_state; struct armada_37xx_dvfs { u32 cpu_freq_max; u8 divider[LOAD_LEVEL_NR]; Loading Loading @@ -136,7 +148,7 @@ static void __init armada37xx_cpufreq_dvfs_setup(struct regmap *base, clk_set_parent(clk, parent); } static void __init armada37xx_cpufreq_disable_dvfs(struct regmap *base) static void armada37xx_cpufreq_disable_dvfs(struct regmap *base) { unsigned int reg = ARMADA_37XX_NB_DYN_MOD, mask = ARMADA_37XX_NB_DFS_EN; Loading @@ -162,10 +174,47 @@ static void __init armada37xx_cpufreq_enable_dvfs(struct regmap *base) regmap_update_bits(base, reg, mask, mask); } static int armada37xx_cpufreq_suspend(struct cpufreq_policy *policy) { struct armada37xx_cpufreq_state *state = armada37xx_cpufreq_state; regmap_read(state->regmap, ARMADA_37XX_NB_L0L1, &state->nb_l0l1); regmap_read(state->regmap, ARMADA_37XX_NB_L2L3, &state->nb_l2l3); regmap_read(state->regmap, ARMADA_37XX_NB_CPU_LOAD, &state->nb_cpu_load); regmap_read(state->regmap, ARMADA_37XX_NB_DYN_MOD, &state->nb_dyn_mod); return 0; } static int armada37xx_cpufreq_resume(struct cpufreq_policy *policy) { struct armada37xx_cpufreq_state *state = armada37xx_cpufreq_state; /* Ensure DVFS is disabled otherwise the following registers are RO */ armada37xx_cpufreq_disable_dvfs(state->regmap); regmap_write(state->regmap, ARMADA_37XX_NB_L0L1, state->nb_l0l1); regmap_write(state->regmap, ARMADA_37XX_NB_L2L3, state->nb_l2l3); regmap_write(state->regmap, ARMADA_37XX_NB_CPU_LOAD, state->nb_cpu_load); /* * NB_DYN_MOD register is the one that actually enable back DVFS if it * was enabled before the suspend operation. This must be done last * otherwise other registers are not writable. */ regmap_write(state->regmap, ARMADA_37XX_NB_DYN_MOD, state->nb_dyn_mod); return 0; } static int __init armada37xx_cpufreq_driver_init(void) { struct cpufreq_dt_platform_data pdata; struct armada_37xx_dvfs *dvfs; struct platform_device *pdev; unsigned long freq; unsigned int cur_frequency; struct regmap *nb_pm_base; struct device *cpu_dev; Loading Loading @@ -207,33 +256,58 @@ static int __init armada37xx_cpufreq_driver_init(void) } dvfs = armada_37xx_cpu_freq_info_get(cur_frequency); if (!dvfs) if (!dvfs) { clk_put(clk); return -EINVAL; } armada37xx_cpufreq_state = kmalloc(sizeof(*armada37xx_cpufreq_state), GFP_KERNEL); if (!armada37xx_cpufreq_state) { clk_put(clk); return -ENOMEM; } armada37xx_cpufreq_state->regmap = nb_pm_base; armada37xx_cpufreq_dvfs_setup(nb_pm_base, clk, dvfs->divider); clk_put(clk); for (load_lvl = ARMADA_37XX_DVFS_LOAD_0; load_lvl < LOAD_LEVEL_NR; load_lvl++) { unsigned long freq = cur_frequency / dvfs->divider[load_lvl]; freq = cur_frequency / dvfs->divider[load_lvl]; ret = dev_pm_opp_add(cpu_dev, freq, 0); if (ret) { if (ret) goto remove_opp; } /* Now that everything is setup, enable the DVFS at hardware level */ armada37xx_cpufreq_enable_dvfs(nb_pm_base); pdata.suspend = armada37xx_cpufreq_suspend; pdata.resume = armada37xx_cpufreq_resume; pdev = platform_device_register_data(NULL, "cpufreq-dt", -1, &pdata, sizeof(pdata)); ret = PTR_ERR_OR_ZERO(pdev); if (ret) goto disable_dvfs; return 0; disable_dvfs: armada37xx_cpufreq_disable_dvfs(nb_pm_base); remove_opp: /* clean-up the already added opp before leaving */ while (load_lvl-- > ARMADA_37XX_DVFS_LOAD_0) { freq = cur_frequency / dvfs->divider[load_lvl]; dev_pm_opp_remove(cpu_dev, freq); } return ret; } } /* Now that everything is setup, enable the DVFS at hardware level */ armada37xx_cpufreq_enable_dvfs(nb_pm_base); kfree(armada37xx_cpufreq_state); pdev = platform_device_register_simple("cpufreq-dt", -1, NULL, 0); return PTR_ERR_OR_ZERO(pdev); return ret; } /* late_initcall, to guarantee the driver is loaded after A37xx clock driver */ late_initcall(armada37xx_cpufreq_driver_init); Loading
drivers/cpufreq/cpufreq-dt-platdev.c +0 −2 Original line number Diff line number Diff line Loading @@ -66,8 +66,6 @@ static const struct of_device_id whitelist[] __initconst = { { .compatible = "renesas,r8a7792", }, { .compatible = "renesas,r8a7793", }, { .compatible = "renesas,r8a7794", }, { .compatible = "renesas,r8a7795", }, { .compatible = "renesas,r8a7796", }, { .compatible = "renesas,sh73a0", }, { .compatible = "rockchip,rk2928", }, Loading
drivers/cpufreq/cpufreq-dt.c +8 −2 Original line number Diff line number Diff line Loading @@ -346,9 +346,15 @@ static int dt_cpufreq_probe(struct platform_device *pdev) if (ret) return ret; if (data && data->have_governor_per_policy) if (data) { if (data->have_governor_per_policy) dt_cpufreq_driver.flags |= CPUFREQ_HAVE_GOVERNOR_PER_POLICY; dt_cpufreq_driver.resume = data->resume; if (data->suspend) dt_cpufreq_driver.suspend = data->suspend; } ret = cpufreq_register_driver(&dt_cpufreq_driver); if (ret) dev_err(&pdev->dev, "failed register driver: %d\n", ret); Loading
drivers/cpufreq/cpufreq-dt.h +5 −0 Original line number Diff line number Diff line Loading @@ -12,8 +12,13 @@ #include <linux/types.h> struct cpufreq_policy; struct cpufreq_dt_platform_data { bool have_governor_per_policy; int (*suspend)(struct cpufreq_policy *policy); int (*resume)(struct cpufreq_policy *policy); }; #endif /* __CPUFREQ_DT_H__ */
drivers/cpufreq/cpufreq.c +32 −31 Original line number Diff line number Diff line Loading @@ -300,8 +300,19 @@ static void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) #endif } static void __cpufreq_notify_transition(struct cpufreq_policy *policy, struct cpufreq_freqs *freqs, unsigned int state) /** * cpufreq_notify_transition - Notify frequency transition and adjust_jiffies. * @policy: cpufreq policy to enable fast frequency switching for. * @freqs: contain details of the frequency update. * @state: set to CPUFREQ_PRECHANGE or CPUFREQ_POSTCHANGE. * * This function calls the transition notifiers and the "adjust_jiffies" * function. It is called twice on all CPU frequency changes that have * external effects. */ static void cpufreq_notify_transition(struct cpufreq_policy *policy, struct cpufreq_freqs *freqs, unsigned int state) { BUG_ON(irqs_disabled()); Loading @@ -313,52 +324,42 @@ static void __cpufreq_notify_transition(struct cpufreq_policy *policy, state, freqs->new); switch (state) { case CPUFREQ_PRECHANGE: /* detect if the driver reported a value as "old frequency" /* * Detect if the driver reported a value as "old frequency" * which is not equal to what the cpufreq core thinks is * "old frequency". */ if (!(cpufreq_driver->flags & CPUFREQ_CONST_LOOPS)) { if ((policy) && (policy->cpu == freqs->cpu) && (policy->cur) && (policy->cur != freqs->old)) { if (policy->cur && (policy->cur != freqs->old)) { pr_debug("Warning: CPU frequency is %u, cpufreq assumed %u kHz\n", freqs->old, policy->cur); freqs->old = policy->cur; } } for_each_cpu(freqs->cpu, policy->cpus) { srcu_notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_PRECHANGE, freqs); } adjust_jiffies(CPUFREQ_PRECHANGE, freqs); break; case CPUFREQ_POSTCHANGE: adjust_jiffies(CPUFREQ_POSTCHANGE, freqs); pr_debug("FREQ: %lu - CPU: %lu\n", (unsigned long)freqs->new, (unsigned long)freqs->cpu); pr_debug("FREQ: %u - CPUs: %*pbl\n", freqs->new, cpumask_pr_args(policy->cpus)); for_each_cpu(freqs->cpu, policy->cpus) { trace_cpu_frequency(freqs->new, freqs->cpu); cpufreq_stats_record_transition(policy, freqs->new); srcu_notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_POSTCHANGE, freqs); if (likely(policy) && likely(policy->cpu == freqs->cpu)) policy->cur = freqs->new; break; } } /** * cpufreq_notify_transition - call notifier chain and adjust_jiffies * on frequency transition. * * This function calls the transition notifiers and the "adjust_jiffies" * function. It is called twice on all CPU frequency changes that have * external effects. */ static void cpufreq_notify_transition(struct cpufreq_policy *policy, struct cpufreq_freqs *freqs, unsigned int state) { for_each_cpu(freqs->cpu, policy->cpus) __cpufreq_notify_transition(policy, freqs, state); cpufreq_stats_record_transition(policy, freqs->new); policy->cur = freqs->new; } } /* Do post notifications when there are chances that transition has failed */ Loading