Pull ARM cpufreq updates for v5.13 from Viresh Kumar: "- Fix typos in s5pv210 cpufreq driver (Bhaskar Chowdhury). - Armada 37xx: Fix cpufreq changing base CPU speed to 800 MHz from 1000 MHz (Pali Rohár and Marek Behún). - cpufreq-dt: Return -EPROBE_DEFER on failure to add table (Quanyang Wang). - Minor cleanup in cppc driver (Tom Saeger). - Add frequency invariance support for CPPC driver and generalize freq invariance support arch-topology driver (Viresh Kumar)." * 'cpufreq/arm/linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm: cpufreq: armada-37xx: Fix module unloading cpufreq: armada-37xx: Remove cur_frequency variable cpufreq: armada-37xx: Fix determining base CPU frequency cpufreq: armada-37xx: Fix driver cleanup when registration failed clk: mvebu: armada-37xx-periph: Fix workaround for switching from L1 to L0 clk: mvebu: armada-37xx-periph: Fix switching CPU freq from 250 Mhz to 1 GHz cpufreq: armada-37xx: Fix the AVS value for load L1 clk: mvebu: armada-37xx-periph: remove .set_parent method for CPU PM clock cpufreq: armada-37xx: Fix setting TBG parent for load levels cpufreq: dt: dev_pm_opp_of_cpumask_add_table() may return -EPROBE_DEFER cpufreq: cppc: simplify default delay_us setting cpufreq: Rudimentary typos fix in the file s5pv210-cpufreq.c cpufreq: CPPC: Add support for frequency invariance arch_topology: Export arch_freq_scale and helpers arch_topology: Allow multiple entities to provide sched_freq_tick() callback arch_topology: Rename freq_scale as arch_freq_scale
Unified · Split
| @@ -17,17 +17,9 @@ int pcibus_to_node(struct pci_bus *bus); · 16 unmodified lines | |||
| 17 | #include <linux/arch_topology.h> | 17 | #include <linux/arch_topology.h> |
| 18 | 18 | ||
| 19 | void update_freq_counters_refs(void); | 19 | void update_freq_counters_refs(void); |
| 20 | void topology_scale_freq_tick(void); | ||
| 21 | |||
| 22 | #ifdef CONFIG_ARM64_AMU_EXTN | ||
| 23 | /* | ||
| 24 | * Replace task scheduler's default counter-based | ||
| 25 | * frequency-invariance scale factor setting. | ||
| 26 | */ | ||
| 27 | #define arch_scale_freq_tick topology_scale_freq_tick | ||
| 28 | #endif /* CONFIG_ARM64_AMU_EXTN */ | ||
| 29 | 20 | ||
| 30 | /* Replace task scheduler's default frequency-invariant accounting */ | 21 | /* Replace task scheduler's default frequency-invariant accounting */ |
| 22 | #define arch_scale_freq_tick topology_scale_freq_tick | ||
| 31 | #define arch_set_freq_scale topology_set_freq_scale | 23 | #define arch_set_freq_scale topology_set_freq_scale |
| 32 | #define arch_scale_freq_capacity topology_get_freq_scale | 24 | #define arch_scale_freq_capacity topology_get_freq_scale |
| 33 | #define arch_scale_freq_invariant topology_scale_freq_invariant | 25 | #define arch_scale_freq_invariant topology_scale_freq_invariant |
| @@ -199,12 +199,47 @@ static int freq_inv_set_max_ratio(int cpu, u64 max_rate, u64 ref_rate) · 198 unmodified lines | |||
| 199 | return 0; | 199 | return 0; |
| 200 | } | 200 | } |
| 201 | 201 | ||
| 202 | static DEFINE_STATIC_KEY_FALSE(amu_fie_key); | 202 | static void amu_scale_freq_tick(void) |
| 203 | #define amu_freq_invariant() static_branch_unlikely(&amu_fie_key) | 203 | { |
| 204 | u64 prev_core_cnt, prev_const_cnt; | ||
| 205 | u64 core_cnt, const_cnt, scale; | ||
| 206 | |||
| 207 | prev_const_cnt = this_cpu_read(arch_const_cycles_prev); | ||
| 208 | prev_core_cnt = this_cpu_read(arch_core_cycles_prev); | ||
| 209 | |||
| 210 | update_freq_counters_refs(); | ||
| 211 | |||
| 212 | const_cnt = this_cpu_read(arch_const_cycles_prev); | ||
| 213 | core_cnt = this_cpu_read(arch_core_cycles_prev); | ||
| 214 | |||
| 215 | if (unlikely(core_cnt <= prev_core_cnt || | ||
| 216 | const_cnt <= prev_const_cnt)) | ||
| 217 | return; | ||
| 218 | |||
| 219 | /* | ||
| 220 | * /\core arch_max_freq_scale | ||
| 221 | * scale = ------- * -------------------- | ||
| 222 | * /\const SCHED_CAPACITY_SCALE | ||
| 223 | * | ||
| 224 | * See validate_cpu_freq_invariance_counters() for details on | ||
| 225 | * arch_max_freq_scale and the use of SCHED_CAPACITY_SHIFT. | ||
| 226 | */ | ||
| 227 | scale = core_cnt - prev_core_cnt; | ||
| 228 | scale *= this_cpu_read(arch_max_freq_scale); | ||
| 229 | scale = div64_u64(scale >> SCHED_CAPACITY_SHIFT, | ||
| 230 | const_cnt - prev_const_cnt); | ||
| 231 | |||
| 232 | scale = min_t(unsigned long, scale, SCHED_CAPACITY_SCALE); | ||
| 233 | this_cpu_write(arch_freq_scale, (unsigned long)scale); | ||
| 234 | } | ||
| 235 | |||
| 236 | static struct scale_freq_data amu_sfd = { | ||
| 237 | .source = SCALE_FREQ_SOURCE_ARCH, | ||
| 238 | .set_freq_scale = amu_scale_freq_tick, | ||
| 239 | }; | ||
| 204 | 240 | ||
| 205 | static void amu_fie_setup(const struct cpumask *cpus) | 241 | static void amu_fie_setup(const struct cpumask *cpus) |
| 206 | { | 242 | { |
| 207 | bool invariant; | ||
| 208 | int cpu; | 243 | int cpu; |
| 209 | 244 | ||
| 210 | /* We are already set since the last insmod of cpufreq driver */ | 245 | /* We are already set since the last insmod of cpufreq driver */ |
| @@ -221,25 +256,10 @@ static void amu_fie_setup(const struct cpumask *cpus) · 10 unmodified lines | |||
| 221 | 256 | ||
| 222 | cpumask_or(amu_fie_cpus, amu_fie_cpus, cpus); | 257 | cpumask_or(amu_fie_cpus, amu_fie_cpus, cpus); |
| 223 | 258 | ||
| 224 | invariant = topology_scale_freq_invariant(); | 259 | topology_set_scale_freq_source(&amu_sfd, amu_fie_cpus); |
| 225 | |||
| 226 | /* We aren't fully invariant yet */ | ||
| 227 | if (!invariant && !cpumask_equal(amu_fie_cpus, cpu_present_mask)) | ||
| 228 | return; | ||
| 229 | |||
| 230 | static_branch_enable(&amu_fie_key); | ||
| 231 | 260 | ||
| 232 | pr_debug("CPUs[%*pbl]: counters will be used for FIE.", | 261 | pr_debug("CPUs[%*pbl]: counters will be used for FIE.", |
| 233 | cpumask_pr_args(cpus)); | 262 | cpumask_pr_args(cpus)); |
| 234 | |||
| 235 | /* | ||
| 236 | * Task scheduler behavior depends on frequency invariance support, | ||
| 237 | * either cpufreq or counter driven. If the support status changes as | ||
| 238 | * a result of counter initialisation and use, retrigger the build of | ||
| 239 | * scheduling domains to ensure the information is propagated properly. | ||
| 240 | */ | ||
| 241 | if (!invariant) | ||
| 242 | rebuild_sched_domains_energy(); | ||
| 243 | } | 263 | } |
| 244 | 264 | ||
| 245 | static int init_amu_fie_callback(struct notifier_block *nb, unsigned long val, | 265 | static int init_amu_fie_callback(struct notifier_block *nb, unsigned long val, |
| @@ -256,8 +276,8 @@ static int init_amu_fie_callback(struct notifier_block *nb, unsigned long val, · 10 unmodified lines | |||
| 256 | * initialized AMU support and enabled invariance. The AMU counters will | 276 | * initialized AMU support and enabled invariance. The AMU counters will |
| 257 | * keep on working just fine in the absence of the cpufreq driver, and | 277 | * keep on working just fine in the absence of the cpufreq driver, and |
| 258 | * for the CPUs for which there are no counters available, the last set | 278 | * for the CPUs for which there are no counters available, the last set |
| 259 | * value of freq_scale will remain valid as that is the frequency those | 279 | * value of arch_freq_scale will remain valid as that is the frequency |
| 260 | * CPUs are running at. | 280 | * those CPUs are running at. |
| 261 | */ | 281 | */ |
| 262 | 282 | ||
| 263 | return 0; | 283 | return 0; |
| @@ -283,53 +303,6 @@ static int __init init_amu_fie(void) · 19 unmodified lines | |||
| 283 | } | 303 | } |
| 284 | core_initcall(init_amu_fie); | 304 | core_initcall(init_amu_fie); |
| 285 | 305 | ||
| 286 | bool arch_freq_counters_available(const struct cpumask *cpus) | ||
| 287 | { | ||
| 288 | return amu_freq_invariant() && | ||
| 289 | cpumask_subset(cpus, amu_fie_cpus); | ||
| 290 | } | ||
| 291 | |||
| 292 | void topology_scale_freq_tick(void) | ||
| 293 | { | ||
| 294 | u64 prev_core_cnt, prev_const_cnt; | ||
| 295 | u64 core_cnt, const_cnt, scale; | ||
| 296 | int cpu = smp_processor_id(); | ||
| 297 | |||
| 298 | if (!amu_freq_invariant()) | ||
| 299 | return; | ||
| 300 | |||
| 301 | if (!cpumask_test_cpu(cpu, amu_fie_cpus)) | ||
| 302 | return; | ||
| 303 | |||
| 304 | prev_const_cnt = this_cpu_read(arch_const_cycles_prev); | ||
| 305 | prev_core_cnt = this_cpu_read(arch_core_cycles_prev); | ||
| 306 | |||
| 307 | update_freq_counters_refs(); | ||
| 308 | |||
| 309 | const_cnt = this_cpu_read(arch_const_cycles_prev); | ||
| 310 | core_cnt = this_cpu_read(arch_core_cycles_prev); | ||
| 311 | |||
| 312 | if (unlikely(core_cnt <= prev_core_cnt || | ||
| 313 | const_cnt <= prev_const_cnt)) | ||
| 314 | return; | ||
| 315 | |||
| 316 | /* | ||
| 317 | * /\core arch_max_freq_scale | ||
| 318 | * scale = ------- * -------------------- | ||
| 319 | * /\const SCHED_CAPACITY_SCALE | ||
| 320 | * | ||
| 321 | * See validate_cpu_freq_invariance_counters() for details on | ||
| 322 | * arch_max_freq_scale and the use of SCHED_CAPACITY_SHIFT. | ||
| 323 | */ | ||
| 324 | scale = core_cnt - prev_core_cnt; | ||
| 325 | scale *= this_cpu_read(arch_max_freq_scale); | ||
| 326 | scale = div64_u64(scale >> SCHED_CAPACITY_SHIFT, | ||
| 327 | const_cnt - prev_const_cnt); | ||
| 328 | |||
| 329 | scale = min_t(unsigned long, scale, SCHED_CAPACITY_SCALE); | ||
| 330 | this_cpu_write(freq_scale, (unsigned long)scale); | ||
| 331 | } | ||
| 332 | |||
| 333 | #ifdef CONFIG_ACPI_CPPC_LIB | 306 | #ifdef CONFIG_ACPI_CPPC_LIB |
| 334 | #include <acpi/cppc_acpi.h> | 307 | #include <acpi/cppc_acpi.h> |
| 335 | 308 | ||
| @@ -21,17 +21,94 @@ · 20 unmodified lines | |||
| 21 | #include <linux/sched.h> | 21 | #include <linux/sched.h> |
| 22 | #include <linux/smp.h> | 22 | #include <linux/smp.h> |
| 23 | 23 | ||
| 24 | static DEFINE_PER_CPU(struct scale_freq_data *, sft_data); | ||
| 25 | static struct cpumask scale_freq_counters_mask; | ||
| 26 | static bool scale_freq_invariant; | ||
| 27 | |||
| 28 | static bool supports_scale_freq_counters(const struct cpumask *cpus) | ||
| 29 | { | ||
| 30 | return cpumask_subset(cpus, &scale_freq_counters_mask); | ||
| 31 | } | ||
| 32 | |||
| 24 | bool topology_scale_freq_invariant(void) | 33 | bool topology_scale_freq_invariant(void) |
| 25 | { | 34 | { |
| 26 | return cpufreq_supports_freq_invariance() || | 35 | return cpufreq_supports_freq_invariance() || |
| 27 | arch_freq_counters_available(cpu_online_mask); | 36 | supports_scale_freq_counters(cpu_online_mask); |
| 28 | } | 37 | } |
| 29 | 38 | ||
| 30 | __weak bool arch_freq_counters_available(const struct cpumask *cpus) | 39 | static void update_scale_freq_invariant(bool status) |
| 31 | { | 40 | { |
| 32 | return false; | 41 | if (scale_freq_invariant == status) |
| 42 | return; | ||
| 43 | |||
| 44 | /* | ||
| 45 | * Task scheduler behavior depends on frequency invariance support, | ||
| 46 | * either cpufreq or counter driven. If the support status changes as | ||
| 47 | * a result of counter initialisation and use, retrigger the build of | ||
| 48 | * scheduling domains to ensure the information is propagated properly. | ||
| 49 | */ | ||
| 50 | if (topology_scale_freq_invariant() == status) { | ||
| 51 | scale_freq_invariant = status; | ||
| 52 | rebuild_sched_domains_energy(); | ||
| 53 | } | ||
| 33 | } | 54 | } |
| 34 | DEFINE_PER_CPU(unsigned long, freq_scale) = SCHED_CAPACITY_SCALE; | 55 | |
| 56 | void topology_set_scale_freq_source(struct scale_freq_data *data, | ||
| 57 | const struct cpumask *cpus) | ||
| 58 | { | ||
| 59 | struct scale_freq_data *sfd; | ||
| 60 | int cpu; | ||
| 61 | |||
| 62 | /* | ||
| 63 | * Avoid calling rebuild_sched_domains() unnecessarily if FIE is | ||
| 64 | * supported by cpufreq. | ||
| 65 | */ | ||
| 66 | if (cpumask_empty(&scale_freq_counters_mask)) | ||
| 67 | scale_freq_invariant = topology_scale_freq_invariant(); | ||
| 68 | |||
| 69 | for_each_cpu(cpu, cpus) { | ||
| 70 | sfd = per_cpu(sft_data, cpu); | ||
| 71 | |||
| 72 | /* Use ARCH provided counters whenever possible */ | ||
| 73 | if (!sfd || sfd->source != SCALE_FREQ_SOURCE_ARCH) { | ||
| 74 | per_cpu(sft_data, cpu) = data; | ||
| 75 | cpumask_set_cpu(cpu, &scale_freq_counters_mask); | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | update_scale_freq_invariant(true); | ||
| 80 | } | ||
| 81 | EXPORT_SYMBOL_GPL(topology_set_scale_freq_source); | ||
| 82 | |||
| 83 | void topology_clear_scale_freq_source(enum scale_freq_source source, | ||
| 84 | const struct cpumask *cpus) | ||
| 85 | { | ||
| 86 | struct scale_freq_data *sfd; | ||
| 87 | int cpu; | ||
| 88 | |||
| 89 | for_each_cpu(cpu, cpus) { | ||
| 90 | sfd = per_cpu(sft_data, cpu); | ||
| 91 | |||
| 92 | if (sfd && sfd->source == source) { | ||
| 93 | per_cpu(sft_data, cpu) = NULL; | ||
| 94 | cpumask_clear_cpu(cpu, &scale_freq_counters_mask); | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | update_scale_freq_invariant(false); | ||
| 99 | } | ||
| 100 | EXPORT_SYMBOL_GPL(topology_clear_scale_freq_source); | ||
| 101 | |||
| 102 | void topology_scale_freq_tick(void) | ||
| 103 | { | ||
| 104 | struct scale_freq_data *sfd = *this_cpu_ptr(&sft_data); | ||
| 105 | |||
| 106 | if (sfd) | ||
| 107 | sfd->set_freq_scale(); | ||
| 108 | } | ||
| 109 | |||
| 110 | DEFINE_PER_CPU(unsigned long, arch_freq_scale) = SCHED_CAPACITY_SCALE; | ||
| 111 | EXPORT_PER_CPU_SYMBOL_GPL(arch_freq_scale); | ||
| 35 | 112 | ||
| 36 | void topology_set_freq_scale(const struct cpumask *cpus, unsigned long cur_freq, | 113 | void topology_set_freq_scale(const struct cpumask *cpus, unsigned long cur_freq, |
| 37 | unsigned long max_freq) | 114 | unsigned long max_freq) |
| @@ -47,13 +124,13 @@ void topology_set_freq_scale(const struct cpumask *cpus, unsigned long cur_freq, · 9 unmodified lines | |||
| 47 | * want to update the scale factor with information from CPUFREQ. | 124 | * want to update the scale factor with information from CPUFREQ. |
| 48 | * Instead the scale factor will be updated from arch_scale_freq_tick. | 125 | * Instead the scale factor will be updated from arch_scale_freq_tick. |
| 49 | */ | 126 | */ |
| 50 | if (arch_freq_counters_available(cpus)) | 127 | if (supports_scale_freq_counters(cpus)) |
| 51 | return; | 128 | return; |
| 52 | 129 | ||
| 53 | scale = (cur_freq << SCHED_CAPACITY_SHIFT) / max_freq; | 130 | scale = (cur_freq << SCHED_CAPACITY_SHIFT) / max_freq; |
| 54 | 131 | ||
| 55 | for_each_cpu(i, cpus) | 132 | for_each_cpu(i, cpus) |
| 56 | per_cpu(freq_scale, i) = scale; | 133 | per_cpu(arch_freq_scale, i) = scale; |
| 57 | } | 134 | } |
| 58 | 135 | ||
| 59 | DEFINE_PER_CPU(unsigned long, cpu_scale) = SCHED_CAPACITY_SCALE; | 136 | DEFINE_PER_CPU(unsigned long, cpu_scale) = SCHED_CAPACITY_SCALE; |
| @@ -84,6 +84,7 @@ struct clk_pm_cpu { · 83 unmodified lines | |||
| 84 | void __iomem *reg_div; | 84 | void __iomem *reg_div; |
| 85 | u8 shift_div; | 85 | u8 shift_div; |
| 86 | struct regmap *nb_pm_base; | 86 | struct regmap *nb_pm_base; |
| 87 | unsigned long l1_expiration; | ||
| 87 | }; | 88 | }; |
| 88 | 89 | ||
| 89 | #define to_clk_double_div(_hw) container_of(_hw, struct clk_double_div, hw) | 90 | #define to_clk_double_div(_hw) container_of(_hw, struct clk_double_div, hw) |
| @@ -440,33 +441,6 @@ static u8 clk_pm_cpu_get_parent(struct clk_hw *hw) · 350 unmodified lines | |||
| 440 | return val; | 441 | return val; |
| 441 | } | 442 | } |
| 442 | 443 | ||
| 443 | static int clk_pm_cpu_set_parent(struct clk_hw *hw, u8 index) | ||
| 444 | { | ||
| 445 | struct clk_pm_cpu *pm_cpu = to_clk_pm_cpu(hw); | ||
| 446 | struct regmap *base = pm_cpu->nb_pm_base; | ||
| 447 | int load_level; | ||
| 448 | |||
| 449 | /* | ||
| 450 | * We set the clock parent only if the DVFS is available but | ||
| 451 | * not enabled. | ||
| 452 | */ | ||
| 453 | if (IS_ERR(base) || armada_3700_pm_dvfs_is_enabled(base)) | ||
| 454 | return -EINVAL; | ||
| 455 | |||
| 456 | /* Set the parent clock for all the load level */ | ||
| 457 | for (load_level = 0; load_level < LOAD_LEVEL_NR; load_level++) { | ||
| 458 | unsigned int reg, mask, val, | ||
| 459 | offset = ARMADA_37XX_NB_TBG_SEL_OFF; | ||
| 460 | |||
| 461 | armada_3700_pm_dvfs_update_regs(load_level, ®, &offset); | ||
| 462 | |||
| 463 | val = index << offset; | ||
| 464 | mask = ARMADA_37XX_NB_TBG_SEL_MASK << offset; | ||
| 465 | regmap_update_bits(base, reg, mask, val); | ||
| 466 | } | ||
| 467 | return 0; | ||
| 468 | } | ||
| 469 | |||
| 470 | static unsigned long clk_pm_cpu_recalc_rate(struct clk_hw *hw, | 444 | static unsigned long clk_pm_cpu_recalc_rate(struct clk_hw *hw, |
| 471 | unsigned long parent_rate) | 445 | unsigned long parent_rate) |
| 472 | { | 446 | { |
| @@ -514,8 +488,10 @@ static long clk_pm_cpu_round_rate(struct clk_hw *hw, unsigned long rate, · 41 unmodified lines | |||
| 514 | } | 488 | } |
| 515 | 489 | ||
| 516 | /* | 490 | /* |
| 517 | * Switching the CPU from the L2 or L3 frequencies (300 and 200 Mhz | 491 | * Workaround when base CPU frequnecy is 1000 or 1200 MHz |
| 518 | * respectively) to L0 frequency (1.2 Ghz) requires a significant | 492 | * |
| 493 | * Switching the CPU from the L2 or L3 frequencies (250/300 or 200 MHz | ||
| 494 | * respectively) to L0 frequency (1/1.2 GHz) requires a significant | ||
| 519 | * amount of time to let VDD stabilize to the appropriate | 495 | * amount of time to let VDD stabilize to the appropriate |
| 520 | * voltage. This amount of time is large enough that it cannot be | 496 | * voltage. This amount of time is large enough that it cannot be |
| 521 | * covered by the hardware countdown register. Due to this, the CPU | 497 | * covered by the hardware countdown register. Due to this, the CPU |
| @@ -525,26 +501,56 @@ static long clk_pm_cpu_round_rate(struct clk_hw *hw, unsigned long rate, · 3 unmodified lines | |||
| 525 | * To work around this problem, we prevent switching directly from the | 501 | * To work around this problem, we prevent switching directly from the |
| 526 | * L2/L3 frequencies to the L0 frequency, and instead switch to the L1 | 502 | * L2/L3 frequencies to the L0 frequency, and instead switch to the L1 |
| 527 | * frequency in-between. The sequence therefore becomes: | 503 | * frequency in-between. The sequence therefore becomes: |
| 528 | * 1. First switch from L2/L3(200/300MHz) to L1(600MHZ) | 504 | * 1. First switch from L2/L3 (200/250/300 MHz) to L1 (500/600 MHz) |
| 529 | * 2. Sleep 20ms for stabling VDD voltage | 505 | * 2. Sleep 20ms for stabling VDD voltage |
| 530 | * 3. Then switch from L1(600MHZ) to L0(1200Mhz). | 506 | * 3. Then switch from L1 (500/600 MHz) to L0 (1000/1200 MHz). |
| 531 | */ | 507 | */ |
| 532 | static void clk_pm_cpu_set_rate_wa(unsigned long rate, struct regmap *base) | 508 | static void clk_pm_cpu_set_rate_wa(struct clk_pm_cpu *pm_cpu, |
| 509 | unsigned int new_level, unsigned long rate, | ||
| 510 | struct regmap *base) | ||
| 533 | { | 511 | { |
| 534 | unsigned int cur_level; | 512 | unsigned int cur_level; |
| 535 | 513 | ||
| 536 | if (rate != 1200 * 1000 * 1000) | ||
| 537 | return; | ||
| 538 | |||
| 539 | regmap_read(base, ARMADA_37XX_NB_CPU_LOAD, &cur_level); | 514 | regmap_read(base, ARMADA_37XX_NB_CPU_LOAD, &cur_level); |
| 540 | cur_level &= ARMADA_37XX_NB_CPU_LOAD_MASK; | 515 | cur_level &= ARMADA_37XX_NB_CPU_LOAD_MASK; |
| 541 | if (cur_level <= ARMADA_37XX_DVFS_LOAD_1) | 516 | |
| 517 | if (cur_level == new_level) | ||
| 518 | return; | ||
| 519 | |||
| 520 | /* | ||
| 521 | * System wants to go to L1 on its own. If we are going from L2/L3, | ||
| 522 | * remember when 20ms will expire. If from L0, set the value so that | ||
| 523 | * next switch to L0 won't have to wait. | ||
| 524 | */ | ||
| 525 | if (new_level == ARMADA_37XX_DVFS_LOAD_1) { | ||
| 526 | if (cur_level == ARMADA_37XX_DVFS_LOAD_0) | ||
| 527 | pm_cpu->l1_expiration = jiffies; | ||
| 528 | else | ||
| 529 | pm_cpu->l1_expiration = jiffies + msecs_to_jiffies(20); | ||
| 542 | return; | 530 | return; |
| 531 | } | ||
| 532 | |||
| 533 | /* | ||
| 534 | * If we are setting to L2/L3, just invalidate L1 expiration time, | ||
| 535 | * sleeping is not needed. | ||
| 536 | */ | ||
| 537 | if (rate < 1000*1000*1000) | ||
| 538 | goto invalidate_l1_exp; | ||
| 539 | |||
| 540 | /* | ||
| 541 | * We are going to L0 with rate >= 1GHz. Check whether we have been at | ||
| 542 | * L1 for long enough time. If not, go to L1 for 20ms. | ||
| 543 | */ | ||
| 544 | if (pm_cpu->l1_expiration && jiffies >= pm_cpu->l1_expiration) | ||
| 545 | goto invalidate_l1_exp; | ||
| 543 | 546 | ||
| 544 | regmap_update_bits(base, ARMADA_37XX_NB_CPU_LOAD, | 547 | regmap_update_bits(base, ARMADA_37XX_NB_CPU_LOAD, |
| 545 | ARMADA_37XX_NB_CPU_LOAD_MASK, | 548 | ARMADA_37XX_NB_CPU_LOAD_MASK, |
| 546 | ARMADA_37XX_DVFS_LOAD_1); | 549 | ARMADA_37XX_DVFS_LOAD_1); |
| 547 | msleep(20); | 550 | msleep(20); |
| 551 | |||
| 552 | invalidate_l1_exp: | ||
| 553 | pm_cpu->l1_expiration = 0; | ||
| 548 | } | 554 | } |
| 549 | 555 | ||
| 550 | static int clk_pm_cpu_set_rate(struct clk_hw *hw, unsigned long rate, | 556 | static int clk_pm_cpu_set_rate(struct clk_hw *hw, unsigned long rate, |
| @@ -578,7 +584,9 @@ static int clk_pm_cpu_set_rate(struct clk_hw *hw, unsigned long rate, · 27 unmodified lines | |||
| 578 | reg = ARMADA_37XX_NB_CPU_LOAD; | 584 | reg = ARMADA_37XX_NB_CPU_LOAD; |
| 579 | mask = ARMADA_37XX_NB_CPU_LOAD_MASK; | 585 | mask = ARMADA_37XX_NB_CPU_LOAD_MASK; |
| 580 | 586 | ||
| 581 | clk_pm_cpu_set_rate_wa(rate, base); | 587 | /* Apply workaround when base CPU frequency is 1000 or 1200 MHz */ |
| 588 | if (parent_rate >= 1000*1000*1000) | ||
| 589 | clk_pm_cpu_set_rate_wa(pm_cpu, load_level, rate, base); | ||
| 582 | 590 | ||
| 583 | regmap_update_bits(base, reg, mask, load_level); | 591 | regmap_update_bits(base, reg, mask, load_level); |
| 584 | 592 | ||
| @@ -592,7 +600,6 @@ static int clk_pm_cpu_set_rate(struct clk_hw *hw, unsigned long rate, · 7 unmodified lines | |||
| 592 | 600 | ||
| 593 | static const struct clk_ops clk_pm_cpu_ops = { | 601 | static const struct clk_ops clk_pm_cpu_ops = { |
| 594 | .get_parent = clk_pm_cpu_get_parent, | 602 | .get_parent = clk_pm_cpu_get_parent, |
| 595 | .set_parent = clk_pm_cpu_set_parent, | ||
| 596 | .round_rate = clk_pm_cpu_round_rate, | 603 | .round_rate = clk_pm_cpu_round_rate, |
| 597 | .set_rate = clk_pm_cpu_set_rate, | 604 | .set_rate = clk_pm_cpu_set_rate, |
| 598 | .recalc_rate = clk_pm_cpu_recalc_rate, | 605 | .recalc_rate = clk_pm_cpu_recalc_rate, |
| @@ -19,6 +19,16 @@ config ACPI_CPPC_CPUFREQ · 18 unmodified lines | |||
| 19 | 19 | ||
| 20 | If in doubt, say N. | 20 | If in doubt, say N. |
| 21 | 21 | ||
| 22 | config ACPI_CPPC_CPUFREQ_FIE | ||
| 23 | bool "Frequency Invariance support for CPPC cpufreq driver" | ||
| 24 | depends on ACPI_CPPC_CPUFREQ && GENERIC_ARCH_TOPOLOGY | ||
| 25 | default y | ||
| 26 | help | ||
| 27 | This extends frequency invariance support in the CPPC cpufreq driver, | ||
| 28 | by using CPPC delivered and reference performance counters. | ||
| 29 | |||
| 30 | If in doubt, say N. | ||
| 31 | |||
| 22 | config ARM_ALLWINNER_SUN50I_CPUFREQ_NVMEM | 32 | config ARM_ALLWINNER_SUN50I_CPUFREQ_NVMEM |
| 23 | tristate "Allwinner nvmem based SUN50I CPUFreq driver" | 33 | tristate "Allwinner nvmem based SUN50I CPUFreq driver" |
| 24 | depends on ARCH_SUNXI | 34 | depends on ARCH_SUNXI |
| @@ -25,6 +25,10 @@ · 24 unmodified lines | |||
| 25 | 25 | ||
| 26 | #include "cpufreq-dt.h" | 26 | #include "cpufreq-dt.h" |
| 27 | 27 | ||
| 28 | /* Clk register set */ | ||
| 29 | #define ARMADA_37XX_CLK_TBG_SEL 0 | ||
| 30 | #define ARMADA_37XX_CLK_TBG_SEL_CPU_OFF 22 | ||
| 31 | |||
| 28 | /* Power management in North Bridge register set */ | 32 | /* Power management in North Bridge register set */ |
| 29 | #define ARMADA_37XX_NB_L0L1 0x18 | 33 | #define ARMADA_37XX_NB_L0L1 0x18 |
| 30 | #define ARMADA_37XX_NB_L2L3 0x1C | 34 | #define ARMADA_37XX_NB_L2L3 0x1C |
| @@ -69,6 +73,8 @@ · 38 unmodified lines | |||
| 69 | #define LOAD_LEVEL_NR 4 | 73 | #define LOAD_LEVEL_NR 4 |
| 70 | 74 | ||
| 71 | #define MIN_VOLT_MV 1000 | 75 | #define MIN_VOLT_MV 1000 |
| 76 | #define MIN_VOLT_MV_FOR_L1_1000MHZ 1108 | ||
| 77 | #define MIN_VOLT_MV_FOR_L1_1200MHZ 1155 | ||
| 72 | 78 | ||
| 73 | /* AVS value for the corresponding voltage (in mV) */ | 79 | /* AVS value for the corresponding voltage (in mV) */ |
| 74 | static int avs_map[] = { | 80 | static int avs_map[] = { |
| @@ -80,6 +86,8 @@ static int avs_map[] = { · 5 unmodified lines | |||
| 80 | }; | 86 | }; |
| 81 | 87 | ||
| 82 | struct armada37xx_cpufreq_state { | 88 | struct armada37xx_cpufreq_state { |
| 89 | struct platform_device *pdev; | ||
| 90 | struct device *cpu_dev; | ||
| 83 | struct regmap *regmap; | 91 | struct regmap *regmap; |
| 84 | u32 nb_l0l1; | 92 | u32 nb_l0l1; |
| 85 | u32 nb_l2l3; | 93 | u32 nb_l2l3; |
| @@ -120,10 +128,15 @@ static struct armada_37xx_dvfs *armada_37xx_cpu_freq_info_get(u32 freq) · 34 unmodified lines | |||
| 120 | * will be configured then the DVFS will be enabled. | 128 | * will be configured then the DVFS will be enabled. |
| 121 | */ | 129 | */ |
| 122 | static void __init armada37xx_cpufreq_dvfs_setup(struct regmap *base, | 130 | static void __init armada37xx_cpufreq_dvfs_setup(struct regmap *base, |
| 123 | struct clk *clk, u8 *divider) | 131 | struct regmap *clk_base, u8 *divider) |
| 124 | { | 132 | { |
| 133 | u32 cpu_tbg_sel; | ||
| 125 | int load_lvl; | 134 | int load_lvl; |
| 126 | struct clk *parent; | 135 | |
| 136 | /* Determine to which TBG clock is CPU connected */ | ||
| 137 | regmap_read(clk_base, ARMADA_37XX_CLK_TBG_SEL, &cpu_tbg_sel); | ||
| 138 | cpu_tbg_sel >>= ARMADA_37XX_CLK_TBG_SEL_CPU_OFF; | ||
| 139 | cpu_tbg_sel &= ARMADA_37XX_NB_TBG_SEL_MASK; | ||
| 127 | 140 | ||
| 128 | for (load_lvl = 0; load_lvl < LOAD_LEVEL_NR; load_lvl++) { | 141 | for (load_lvl = 0; load_lvl < LOAD_LEVEL_NR; load_lvl++) { |
| 129 | unsigned int reg, mask, val, offset = 0; | 142 | unsigned int reg, mask, val, offset = 0; |
| @@ -142,6 +155,11 @@ static void __init armada37xx_cpufreq_dvfs_setup(struct regmap *base, · 12 unmodified lines | |||
| 142 | mask = (ARMADA_37XX_NB_CLK_SEL_MASK | 155 | mask = (ARMADA_37XX_NB_CLK_SEL_MASK |
| 143 | << ARMADA_37XX_NB_CLK_SEL_OFF); | 156 | << ARMADA_37XX_NB_CLK_SEL_OFF); |
| 144 | 157 | ||
| 158 | /* Set TBG index, for all levels we use the same TBG */ | ||
| 159 | val = cpu_tbg_sel << ARMADA_37XX_NB_TBG_SEL_OFF; | ||
| 160 | mask = (ARMADA_37XX_NB_TBG_SEL_MASK | ||
| 161 | << ARMADA_37XX_NB_TBG_SEL_OFF); | ||
| 162 | |||
| 145 | /* | 163 | /* |
| 146 | * Set cpu divider based on the pre-computed array in | 164 | * Set cpu divider based on the pre-computed array in |
| 147 | * order to have balanced step. | 165 | * order to have balanced step. |
| @@ -160,14 +178,6 @@ static void __init armada37xx_cpufreq_dvfs_setup(struct regmap *base, · 12 unmodified lines | |||
| 160 | 178 | ||
| 161 | regmap_update_bits(base, reg, mask, val); | 179 | regmap_update_bits(base, reg, mask, val); |
| 162 | } | 180 | } |
| 163 | |||
| 164 | /* | ||
| 165 | * Set cpu clock source, for all the level we keep the same | ||
| 166 | * clock source that the one already configured. For this one | ||
| 167 | * we need to use the clock framework | ||
| 168 | */ | ||
| 169 | parent = clk_get_parent(clk); | ||
| 170 | clk_set_parent(clk, parent); | ||
| 171 | } | 181 | } |
| 172 | 182 | ||
| 173 | /* | 183 | /* |
| @@ -202,6 +212,8 @@ static u32 armada_37xx_avs_val_match(int target_vm) · 28 unmodified lines | |||
| 202 | * - L2 & L3 voltage should be about 150mv smaller than L0 voltage. | 212 | * - L2 & L3 voltage should be about 150mv smaller than L0 voltage. |
| 203 | * This function calculates L1 & L2 & L3 AVS values dynamically based | 213 | * This function calculates L1 & L2 & L3 AVS values dynamically based |
| 204 | * on L0 voltage and fill all AVS values to the AVS value table. | 214 | * on L0 voltage and fill all AVS values to the AVS value table. |
| 215 | * When base CPU frequency is 1000 or 1200 MHz then there is additional | ||
| 216 | * minimal avs value for load L1. | ||
| 205 | */ | 217 | */ |
| 206 | static void __init armada37xx_cpufreq_avs_configure(struct regmap *base, | 218 | static void __init armada37xx_cpufreq_avs_configure(struct regmap *base, |
| 207 | struct armada_37xx_dvfs *dvfs) | 219 | struct armada_37xx_dvfs *dvfs) |
| @@ -233,6 +245,19 @@ static void __init armada37xx_cpufreq_avs_configure(struct regmap *base, · 25 unmodified lines | |||
| 233 | for (load_level = 1; load_level < LOAD_LEVEL_NR; load_level++) | 245 | for (load_level = 1; load_level < LOAD_LEVEL_NR; load_level++) |
| 234 | dvfs->avs[load_level] = avs_min; | 246 | dvfs->avs[load_level] = avs_min; |
| 235 | 247 | ||
| 248 | /* | ||
| 249 | * Set the avs values for load L0 and L1 when base CPU frequency | ||
| 250 | * is 1000/1200 MHz to its typical initial values according to | ||
| 251 | * the Armada 3700 Hardware Specifications. | ||
| 252 | */ | ||
| 253 | if (dvfs->cpu_freq_max >= 1000*1000*1000) { | ||
| 254 | if (dvfs->cpu_freq_max >= 1200*1000*1000) | ||
| 255 | avs_min = armada_37xx_avs_val_match(MIN_VOLT_MV_FOR_L1_1200MHZ); | ||
| 256 | else | ||
| 257 | avs_min = armada_37xx_avs_val_match(MIN_VOLT_MV_FOR_L1_1000MHZ); | ||
| 258 | dvfs->avs[0] = dvfs->avs[1] = avs_min; | ||
| 259 | } | ||
| 260 | |||
| 236 | return; | 261 | return; |
| 237 | } | 262 | } |
| 238 | 263 | ||
| @@ -252,6 +277,26 @@ static void __init armada37xx_cpufreq_avs_configure(struct regmap *base, · 13 unmodified lines | |||
| 252 | target_vm = avs_map[l0_vdd_min] - 150; | 277 | target_vm = avs_map[l0_vdd_min] - 150; |
| 253 | target_vm = target_vm > MIN_VOLT_MV ? target_vm : MIN_VOLT_MV; | 278 | target_vm = target_vm > MIN_VOLT_MV ? target_vm : MIN_VOLT_MV; |
| 254 | dvfs->avs[2] = dvfs->avs[3] = armada_37xx_avs_val_match(target_vm); | 279 | dvfs->avs[2] = dvfs->avs[3] = armada_37xx_avs_val_match(target_vm); |
| 280 | |||
| 281 | /* | ||
| 282 | * Fix the avs value for load L1 when base CPU frequency is 1000/1200 MHz, | ||
| 283 | * otherwise the CPU gets stuck when switching from load L1 to load L0. | ||
| 284 | * Also ensure that avs value for load L1 is not higher than for L0. | ||
| 285 | */ | ||
| 286 | if (dvfs->cpu_freq_max >= 1000*1000*1000) { | ||
| 287 | u32 avs_min_l1; | ||
| 288 | |||
| 289 | if (dvfs->cpu_freq_max >= 1200*1000*1000) | ||
| 290 | avs_min_l1 = armada_37xx_avs_val_match(MIN_VOLT_MV_FOR_L1_1200MHZ); | ||
| 291 | else | ||
| 292 | avs_min_l1 = armada_37xx_avs_val_match(MIN_VOLT_MV_FOR_L1_1000MHZ); | ||
| 293 | |||
| 294 | if (avs_min_l1 > dvfs->avs[0]) | ||
| 295 | avs_min_l1 = dvfs->avs[0]; | ||
| 296 | |||
| 297 | if (dvfs->avs[1] < avs_min_l1) | ||
| 298 | dvfs->avs[1] = avs_min_l1; | ||
| 299 | } | ||
| 255 | } | 300 | } |
| 256 | 301 | ||
| 257 | static void __init armada37xx_cpufreq_avs_setup(struct regmap *base, | 302 | static void __init armada37xx_cpufreq_avs_setup(struct regmap *base, |
| @@ -357,12 +402,17 @@ static int __init armada37xx_cpufreq_driver_init(void) · 99 unmodified lines | |||
| 357 | struct armada_37xx_dvfs *dvfs; | 402 | struct armada_37xx_dvfs *dvfs; |
| 358 | struct platform_device *pdev; | 403 | struct platform_device *pdev; |
| 359 | unsigned long freq; | 404 | unsigned long freq; |
| 360 | unsigned int cur_frequency, base_frequency; | 405 | unsigned int base_frequency; |
| 361 | struct regmap *nb_pm_base, *avs_base; | 406 | struct regmap *nb_clk_base, *nb_pm_base, *avs_base; |
| 362 | struct device *cpu_dev; | 407 | struct device *cpu_dev; |
| 363 | int load_lvl, ret; | 408 | int load_lvl, ret; |
| 364 | struct clk *clk, *parent; | 409 | struct clk *clk, *parent; |
| 365 | 410 | ||
| 411 | nb_clk_base = | ||
| 412 | syscon_regmap_lookup_by_compatible("marvell,armada-3700-periph-clock-nb"); | ||
| 413 | if (IS_ERR(nb_clk_base)) | ||
| 414 | return -ENODEV; | ||
| 415 | |||
| 366 | nb_pm_base = | 416 | nb_pm_base = |
| 367 | syscon_regmap_lookup_by_compatible("marvell,armada-3700-nb-pm"); | 417 | syscon_regmap_lookup_by_compatible("marvell,armada-3700-nb-pm"); |
| 368 | 418 | ||
| @@ -413,15 +463,7 @@ static int __init armada37xx_cpufreq_driver_init(void) · 44 unmodified lines | |||
| 413 | return -EINVAL; | 463 | return -EINVAL; |
| 414 | } | 464 | } |
| 415 | 465 | ||
| 416 | /* Get nominal (current) CPU frequency */ | 466 | dvfs = armada_37xx_cpu_freq_info_get(base_frequency); |
| 417 | cur_frequency = clk_get_rate(clk); | ||
| 418 | if (!cur_frequency) { | ||
| 419 | dev_err(cpu_dev, "Failed to get clock rate for CPU\n"); | ||
| 420 | clk_put(clk); | ||
| 421 | return -EINVAL; | ||
| 422 | } | ||
| 423 | |||
| 424 | dvfs = armada_37xx_cpu_freq_info_get(cur_frequency); | ||
| 425 | if (!dvfs) { | 467 | if (!dvfs) { |
| 426 | clk_put(clk); | 468 | clk_put(clk); |
| 427 | return -EINVAL; | 469 | return -EINVAL; |
| @@ -439,7 +481,7 @@ static int __init armada37xx_cpufreq_driver_init(void) · 11 unmodified lines | |||
| 439 | armada37xx_cpufreq_avs_configure(avs_base, dvfs); | 481 | armada37xx_cpufreq_avs_configure(avs_base, dvfs); |
| 440 | armada37xx_cpufreq_avs_setup(avs_base, dvfs); | 482 | armada37xx_cpufreq_avs_setup(avs_base, dvfs); |
| 441 | 483 | ||
| 442 | armada37xx_cpufreq_dvfs_setup(nb_pm_base, clk, dvfs->divider); | 484 | armada37xx_cpufreq_dvfs_setup(nb_pm_base, nb_clk_base, dvfs->divider); |
| 443 | clk_put(clk); | 485 | clk_put(clk); |
| 444 | 486 | ||
| 445 | for (load_lvl = ARMADA_37XX_DVFS_LOAD_0; load_lvl < LOAD_LEVEL_NR; | 487 | for (load_lvl = ARMADA_37XX_DVFS_LOAD_0; load_lvl < LOAD_LEVEL_NR; |
| @@ -466,6 +508,9 @@ static int __init armada37xx_cpufreq_driver_init(void) · 20 unmodified lines | |||
| 466 | if (ret) | 508 | if (ret) |
| 467 | goto disable_dvfs; | 509 | goto disable_dvfs; |
| 468 | 510 | ||
| 511 | armada37xx_cpufreq_state->cpu_dev = cpu_dev; | ||
| 512 | armada37xx_cpufreq_state->pdev = pdev; | ||
| 513 | platform_set_drvdata(pdev, dvfs); | ||
| 469 | return 0; | 514 | return 0; |
| 470 | 515 | ||
| 471 | disable_dvfs: | 516 | disable_dvfs: |
| @@ -473,7 +518,7 @@ disable_dvfs: · 1 unmodified line | |||
| 473 | remove_opp: | 518 | remove_opp: |
| 474 | /* clean-up the already added opp before leaving */ | 519 | /* clean-up the already added opp before leaving */ |
| 475 | while (load_lvl-- > ARMADA_37XX_DVFS_LOAD_0) { | 520 | while (load_lvl-- > ARMADA_37XX_DVFS_LOAD_0) { |
| 476 | freq = cur_frequency / dvfs->divider[load_lvl]; | 521 | freq = base_frequency / dvfs->divider[load_lvl]; |
| 477 | dev_pm_opp_remove(cpu_dev, freq); | 522 | dev_pm_opp_remove(cpu_dev, freq); |
| 478 | } | 523 | } |
| 479 | 524 | ||
| @@ -484,6 +529,26 @@ remove_opp: · 4 unmodified lines | |||
| 484 | /* late_initcall, to guarantee the driver is loaded after A37xx clock driver */ | 529 | /* late_initcall, to guarantee the driver is loaded after A37xx clock driver */ |
| 485 | late_initcall(armada37xx_cpufreq_driver_init); | 530 | late_initcall(armada37xx_cpufreq_driver_init); |
| 486 | 531 | ||
| 532 | static void __exit armada37xx_cpufreq_driver_exit(void) | ||
| 533 | { | ||
| 534 | struct platform_device *pdev = armada37xx_cpufreq_state->pdev; | ||
| 535 | struct armada_37xx_dvfs *dvfs = platform_get_drvdata(pdev); | ||
| 536 | unsigned long freq; | ||
| 537 | int load_lvl; | ||
| 538 | |||
| 539 | platform_device_unregister(pdev); | ||
| 540 | |||
| 541 | armada37xx_cpufreq_disable_dvfs(armada37xx_cpufreq_state->regmap); | ||
| 542 | |||
| 543 | for (load_lvl = ARMADA_37XX_DVFS_LOAD_0; load_lvl < LOAD_LEVEL_NR; load_lvl++) { | ||
| 544 | freq = dvfs->cpu_freq_max / dvfs->divider[load_lvl]; | ||
| 545 | dev_pm_opp_remove(armada37xx_cpufreq_state->cpu_dev, freq); | ||
| 546 | } | ||
| 547 | |||
| 548 | kfree(armada37xx_cpufreq_state); | ||
| 549 | } | ||
| 550 | module_exit(armada37xx_cpufreq_driver_exit); | ||
| 551 | |||
| 487 | static const struct of_device_id __maybe_unused armada37xx_cpufreq_of_match[] = { | 552 | static const struct of_device_id __maybe_unused armada37xx_cpufreq_of_match[] = { |
| 488 | { .compatible = "marvell,armada-3700-nb-pm" }, | 553 | { .compatible = "marvell,armada-3700-nb-pm" }, |
| 489 | { }, | 554 | { }, |
| @@ -10,14 +10,18 @@ · 9 unmodified lines | |||
| 10 | 10 | ||
| 11 | #define pr_fmt(fmt) "CPPC Cpufreq:" fmt | 11 | #define pr_fmt(fmt) "CPPC Cpufreq:" fmt |
| 12 | 12 | ||
| 13 | #include <linux/arch_topology.h> | ||
| 13 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
| 14 | #include <linux/module.h> | 15 | #include <linux/module.h> |
| 15 | #include <linux/delay.h> | 16 | #include <linux/delay.h> |
| 16 | #include <linux/cpu.h> | 17 | #include <linux/cpu.h> |
| 17 | #include <linux/cpufreq.h> | 18 | #include <linux/cpufreq.h> |
| 18 | #include <linux/dmi.h> | 19 | #include <linux/dmi.h> |
| 20 | #include <linux/irq_work.h> | ||
| 21 | #include <linux/kthread.h> | ||
| 19 | #include <linux/time.h> | 22 | #include <linux/time.h> |
| 20 | #include <linux/vmalloc.h> | 23 | #include <linux/vmalloc.h> |
| 24 | #include <uapi/linux/sched/types.h> | ||
| 21 | 25 | ||
| 22 | #include <asm/unaligned.h> | 26 | #include <asm/unaligned.h> |
| 23 | 27 | ||
| @@ -57,6 +61,204 @@ static struct cppc_workaround_oem_info wa_info[] = { · 33 unmodified lines | |||
| 57 | } | 61 | } |
| 58 | }; | 62 | }; |
| 59 | 63 | ||
| 64 | #ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE | ||
| 65 | |||
| 66 | /* Frequency invariance support */ | ||
| 67 | struct cppc_freq_invariance { | ||
| 68 | int cpu; | ||
| 69 | struct irq_work irq_work; | ||
| 70 | struct kthread_work work; | ||
| 71 | struct cppc_perf_fb_ctrs prev_perf_fb_ctrs; | ||
| 72 | struct cppc_cpudata *cpu_data; | ||
| 73 | }; | ||
| 74 | |||
| 75 | static DEFINE_PER_CPU(struct cppc_freq_invariance, cppc_freq_inv); | ||
| 76 | static struct kthread_worker *kworker_fie; | ||
| 77 | static bool fie_disabled; | ||
| 78 | |||
| 79 | static struct cpufreq_driver cppc_cpufreq_driver; | ||
| 80 | static unsigned int hisi_cppc_cpufreq_get_rate(unsigned int cpu); | ||
| 81 | static int cppc_perf_from_fbctrs(struct cppc_cpudata *cpu_data, | ||
| 82 | struct cppc_perf_fb_ctrs fb_ctrs_t0, | ||
| 83 | struct cppc_perf_fb_ctrs fb_ctrs_t1); | ||
| 84 | |||
| 85 | /** | ||
| 86 | * cppc_scale_freq_workfn - CPPC arch_freq_scale updater for frequency invariance | ||
| 87 | * @work: The work item. | ||
| 88 | * | ||
| 89 | * The CPPC driver register itself with the topology core to provide its own | ||
| 90 | * implementation (cppc_scale_freq_tick()) of topology_scale_freq_tick() which | ||
| 91 | * gets called by the scheduler on every tick. | ||
| 92 | * | ||
| 93 | * Note that the arch specific counters have higher priority than CPPC counters, | ||
| 94 | * if available, though the CPPC driver doesn't need to have any special | ||
| 95 | * handling for that. | ||
| 96 | * | ||
| 97 | * On an invocation of cppc_scale_freq_tick(), we schedule an irq work (since we | ||
| 98 | * reach here from hard-irq context), which then schedules a normal work item | ||
| 99 | * and cppc_scale_freq_workfn() updates the per_cpu arch_freq_scale variable | ||
| 100 | * based on the counter updates since the last tick. | ||
| 101 | */ | ||
| 102 | static void cppc_scale_freq_workfn(struct kthread_work *work) | ||
| 103 | { | ||
| 104 | struct cppc_freq_invariance *cppc_fi; | ||
| 105 | struct cppc_perf_fb_ctrs fb_ctrs = {0}; | ||
| 106 | struct cppc_cpudata *cpu_data; | ||
| 107 | unsigned long local_freq_scale; | ||
| 108 | u64 perf; | ||
| 109 | |||
| 110 | cppc_fi = container_of(work, struct cppc_freq_invariance, work); | ||
| 111 | cpu_data = cppc_fi->cpu_data; | ||
| 112 | |||
| 113 | if (cppc_get_perf_ctrs(cppc_fi->cpu, &fb_ctrs)) { | ||
| 114 | pr_warn("%s: failed to read perf counters\n", __func__); | ||
| 115 | return; | ||
| 116 | } | ||
| 117 | |||
| 118 | cppc_fi->prev_perf_fb_ctrs = fb_ctrs; | ||
| 119 | perf = cppc_perf_from_fbctrs(cpu_data, cppc_fi->prev_perf_fb_ctrs, | ||
| 120 | fb_ctrs); | ||
| 121 | |||
| 122 | perf <<= SCHED_CAPACITY_SHIFT; | ||
| 123 | local_freq_scale = div64_u64(perf, cpu_data->perf_caps.highest_perf); | ||
| 124 | if (WARN_ON(local_freq_scale > 1024)) | ||
| 125 | local_freq_scale = 1024; | ||
| 126 | |||
| 127 | per_cpu(arch_freq_scale, cppc_fi->cpu) = local_freq_scale; | ||
| 128 | } | ||
| 129 | |||
| 130 | static void cppc_irq_work(struct irq_work *irq_work) | ||
| 131 | { | ||
| 132 | struct cppc_freq_invariance *cppc_fi; | ||
| 133 | |||
| 134 | cppc_fi = container_of(irq_work, struct cppc_freq_invariance, irq_work); | ||
| 135 | kthread_queue_work(kworker_fie, &cppc_fi->work); | ||
| 136 | } | ||
| 137 | |||
| 138 | static void cppc_scale_freq_tick(void) | ||
| 139 | { | ||
| 140 | struct cppc_freq_invariance *cppc_fi = &per_cpu(cppc_freq_inv, smp_processor_id()); | ||
| 141 | |||
| 142 | /* | ||
| 143 | * cppc_get_perf_ctrs() can potentially sleep, call that from the right | ||
| 144 | * context. | ||
| 145 | */ | ||
| 146 | irq_work_queue(&cppc_fi->irq_work); | ||
| 147 | } | ||
| 148 | |||
| 149 | static struct scale_freq_data cppc_sftd = { | ||
| 150 | .source = SCALE_FREQ_SOURCE_CPPC, | ||
| 151 | .set_freq_scale = cppc_scale_freq_tick, | ||
| 152 | }; | ||
| 153 | |||
| 154 | static void cppc_freq_invariance_policy_init(struct cpufreq_policy *policy, | ||
| 155 | struct cppc_cpudata *cpu_data) | ||
| 156 | { | ||
| 157 | struct cppc_perf_fb_ctrs fb_ctrs = {0}; | ||
| 158 | struct cppc_freq_invariance *cppc_fi; | ||
| 159 | int i, ret; | ||
| 160 | |||
| 161 | if (cppc_cpufreq_driver.get == hisi_cppc_cpufreq_get_rate) | ||
| 162 | return; | ||
| 163 | |||
| 164 | if (fie_disabled) | ||
| 165 | return; | ||
| 166 | |||
| 167 | for_each_cpu(i, policy->cpus) { | ||
| 168 | cppc_fi = &per_cpu(cppc_freq_inv, i); | ||
| 169 | cppc_fi->cpu = i; | ||
| 170 | cppc_fi->cpu_data = cpu_data; | ||
| 171 | kthread_init_work(&cppc_fi->work, cppc_scale_freq_workfn); | ||
| 172 | init_irq_work(&cppc_fi->irq_work, cppc_irq_work); | ||
| 173 | |||
| 174 | ret = cppc_get_perf_ctrs(i, &fb_ctrs); | ||
| 175 | if (ret) { | ||
| 176 | pr_warn("%s: failed to read perf counters: %d\n", | ||
| 177 | __func__, ret); | ||
| 178 | fie_disabled = true; | ||
| 179 | } else { | ||
| 180 | cppc_fi->prev_perf_fb_ctrs = fb_ctrs; | ||
| 181 | } | ||
| 182 | } | ||
| 183 | } | ||
| 184 | |||
| 185 | static void __init cppc_freq_invariance_init(void) | ||
| 186 | { | ||
| 187 | struct sched_attr attr = { | ||
| 188 | .size = sizeof(struct sched_attr), | ||
| 189 | .sched_policy = SCHED_DEADLINE, | ||
| 190 | .sched_nice = 0, | ||
| 191 | .sched_priority = 0, | ||
| 192 | /* | ||
| 193 | * Fake (unused) bandwidth; workaround to "fix" | ||
| 194 | * priority inheritance. | ||
| 195 | */ | ||
| 196 | .sched_runtime = 1000000, | ||
| 197 | .sched_deadline = 10000000, | ||
| 198 | .sched_period = 10000000, | ||
| 199 | }; | ||
| 200 | int ret; | ||
| 201 | |||
| 202 | if (cppc_cpufreq_driver.get == hisi_cppc_cpufreq_get_rate) | ||
| 203 | return; | ||
| 204 | |||
| 205 | if (fie_disabled) | ||
| 206 | return; | ||
| 207 | |||
| 208 | kworker_fie = kthread_create_worker(0, "cppc_fie"); | ||
| 209 | if (IS_ERR(kworker_fie)) | ||
| 210 | return; | ||
| 211 | |||
| 212 | ret = sched_setattr_nocheck(kworker_fie->task, &attr); | ||
| 213 | if (ret) { | ||
| 214 | pr_warn("%s: failed to set SCHED_DEADLINE: %d\n", __func__, | ||
| 215 | ret); | ||
| 216 | kthread_destroy_worker(kworker_fie); | ||
| 217 | return; | ||
| 218 | } | ||
| 219 | |||
| 220 | /* Register for freq-invariance */ | ||
| 221 | topology_set_scale_freq_source(&cppc_sftd, cpu_present_mask); | ||
| 222 | } | ||
| 223 | |||
| 224 | static void cppc_freq_invariance_exit(void) | ||
| 225 | { | ||
| 226 | struct cppc_freq_invariance *cppc_fi; | ||
| 227 | int i; | ||
| 228 | |||
| 229 | if (cppc_cpufreq_driver.get == hisi_cppc_cpufreq_get_rate) | ||
| 230 | return; | ||
| 231 | |||
| 232 | if (fie_disabled) | ||
| 233 | return; | ||
| 234 | |||
| 235 | topology_clear_scale_freq_source(SCALE_FREQ_SOURCE_CPPC, cpu_present_mask); | ||
| 236 | |||
| 237 | for_each_possible_cpu(i) { | ||
| 238 | cppc_fi = &per_cpu(cppc_freq_inv, i); | ||
| 239 | irq_work_sync(&cppc_fi->irq_work); | ||
| 240 | } | ||
| 241 | |||
| 242 | kthread_destroy_worker(kworker_fie); | ||
| 243 | kworker_fie = NULL; | ||
| 244 | } | ||
| 245 | |||
| 246 | #else | ||
| 247 | static inline void | ||
| 248 | cppc_freq_invariance_policy_init(struct cpufreq_policy *policy, | ||
| 249 | struct cppc_cpudata *cpu_data) | ||
| 250 | { | ||
| 251 | } | ||
| 252 | |||
| 253 | static inline void cppc_freq_invariance_init(void) | ||
| 254 | { | ||
| 255 | } | ||
| 256 | |||
| 257 | static inline void cppc_freq_invariance_exit(void) | ||
| 258 | { | ||
| 259 | } | ||
| 260 | #endif /* CONFIG_ACPI_CPPC_CPUFREQ_FIE */ | ||
| 261 | |||
| 60 | /* Callback function used to retrieve the max frequency from DMI */ | 262 | /* Callback function used to retrieve the max frequency from DMI */ |
| 61 | static void cppc_find_dmi_mhz(const struct dmi_header *dm, void *private) | 263 | static void cppc_find_dmi_mhz(const struct dmi_header *dm, void *private) |
| 62 | { | 264 | { |
| @@ -216,26 +418,16 @@ static unsigned int cppc_cpufreq_get_transition_delay_us(unsigned int cpu) · 153 unmodified lines | |||
| 216 | { | 418 | { |
| 217 | unsigned long implementor = read_cpuid_implementor(); | 419 | unsigned long implementor = read_cpuid_implementor(); |
| 218 | unsigned long part_num = read_cpuid_part_number(); | 420 | unsigned long part_num = read_cpuid_part_number(); |
| 219 | unsigned int delay_us = 0; | ||
| 220 | 421 | ||
| 221 | switch (implementor) { | 422 | switch (implementor) { |
| 222 | case ARM_CPU_IMP_QCOM: | 423 | case ARM_CPU_IMP_QCOM: |
| 223 | switch (part_num) { | 424 | switch (part_num) { |
| 224 | case QCOM_CPU_PART_FALKOR_V1: | 425 | case QCOM_CPU_PART_FALKOR_V1: |
| 225 | case QCOM_CPU_PART_FALKOR: | 426 | case QCOM_CPU_PART_FALKOR: |
| 226 | delay_us = 10000; | 427 | return 10000; |
| 227 | break; | ||
| 228 | default: | ||
| 229 | delay_us = cppc_get_transition_latency(cpu) / NSEC_PER_USEC; | ||
| 230 | break; | ||
| 231 | } | 428 | } |
| 232 | break; | ||
| 233 | default: | ||
| 234 | delay_us = cppc_get_transition_latency(cpu) / NSEC_PER_USEC; | ||
| 235 | break; | ||
| 236 | } | 429 | } |
| 237 | 430 | return cppc_get_transition_latency(cpu) / NSEC_PER_USEC; | |
| 238 | return delay_us; | ||
| 239 | } | 431 | } |
| 240 | 432 | ||
| 241 | #else | 433 | #else |
| @@ -355,9 +547,12 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) · 113 unmodified lines | |||
| 355 | cpu_data->perf_ctrls.desired_perf = caps->highest_perf; | 547 | cpu_data->perf_ctrls.desired_perf = caps->highest_perf; |
| 356 | 548 | ||
| 357 | ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls); | 549 | ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls); |
| 358 | if (ret) | 550 | if (ret) { |
| 359 | pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n", | 551 | pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n", |
| 360 | caps->highest_perf, cpu, ret); | 552 | caps->highest_perf, cpu, ret); |
| 553 | } else { | ||
| 554 | cppc_freq_invariance_policy_init(policy, cpu_data); | ||
| 555 | } | ||
| 361 | 556 | ||
| 362 | return ret; | 557 | return ret; |
| 363 | } | 558 | } |
| @@ -370,12 +565,12 @@ static inline u64 get_delta(u64 t1, u64 t0) · 6 unmodified lines | |||
| 370 | return (u32)t1 - (u32)t0; | 565 | return (u32)t1 - (u32)t0; |
| 371 | } | 566 | } |
| 372 | 567 | ||
| 373 | static int cppc_get_rate_from_fbctrs(struct cppc_cpudata *cpu_data, | 568 | static int cppc_perf_from_fbctrs(struct cppc_cpudata *cpu_data, |
| 374 | struct cppc_perf_fb_ctrs fb_ctrs_t0, | 569 | struct cppc_perf_fb_ctrs fb_ctrs_t0, |
| 375 | struct cppc_perf_fb_ctrs fb_ctrs_t1) | 570 | struct cppc_perf_fb_ctrs fb_ctrs_t1) |
| 376 | { | 571 | { |
| 377 | u64 delta_reference, delta_delivered; | 572 | u64 delta_reference, delta_delivered; |
| 378 | u64 reference_perf, delivered_perf; | 573 | u64 reference_perf; |
| 379 | 574 | ||
| 380 | reference_perf = fb_ctrs_t0.reference_perf; | 575 | reference_perf = fb_ctrs_t0.reference_perf; |
| 381 | 576 | ||
| @@ -384,12 +579,21 @@ static int cppc_get_rate_from_fbctrs(struct cppc_cpudata *cpu_data, · 2 unmodified lines | |||
| 384 | delta_delivered = get_delta(fb_ctrs_t1.delivered, | 579 | delta_delivered = get_delta(fb_ctrs_t1.delivered, |
| 385 | fb_ctrs_t0.delivered); | 580 | fb_ctrs_t0.delivered); |
| 386 | 581 | ||
| 387 | /* Check to avoid divide-by zero */ | 582 | /* Check to avoid divide-by zero and invalid delivered_perf */ |
| 388 | if (delta_reference || delta_delivered) | 583 | if (!delta_reference || !delta_delivered) |
| 389 | delivered_perf = (reference_perf * delta_delivered) / | 584 | return cpu_data->perf_ctrls.desired_perf; |
| 390 | delta_reference; | 585 | |
| 391 | else | 586 | return (reference_perf * delta_delivered) / delta_reference; |
| 392 | delivered_perf = cpu_data->perf_ctrls.desired_perf; | 587 | } |
| 588 | |||
| 589 | static int cppc_get_rate_from_fbctrs(struct cppc_cpudata *cpu_data, | ||
| 590 | struct cppc_perf_fb_ctrs fb_ctrs_t0, | ||
| 591 | struct cppc_perf_fb_ctrs fb_ctrs_t1) | ||
| 592 | { | ||
| 593 | u64 delivered_perf; | ||
| 594 | |||
| 595 | delivered_perf = cppc_perf_from_fbctrs(cpu_data, fb_ctrs_t0, | ||
| 596 | fb_ctrs_t1); | ||
| 393 | 597 | ||
| 394 | return cppc_cpufreq_perf_to_khz(cpu_data, delivered_perf); | 598 | return cppc_cpufreq_perf_to_khz(cpu_data, delivered_perf); |
| 395 | } | 599 | } |
| @@ -514,6 +718,8 @@ static void cppc_check_hisi_workaround(void) · 118 unmodified lines | |||
| 514 | 718 | ||
| 515 | static int __init cppc_cpufreq_init(void) | 719 | static int __init cppc_cpufreq_init(void) |
| 516 | { | 720 | { |
| 721 | int ret; | ||
| 722 | |||
| 517 | if ((acpi_disabled) || !acpi_cpc_valid()) | 723 | if ((acpi_disabled) || !acpi_cpc_valid()) |
| 518 | return -ENODEV; | 724 | return -ENODEV; |
| 519 | 725 | ||
| @@ -521,7 +727,11 @@ static int __init cppc_cpufreq_init(void) · 1 unmodified line | |||
| 521 | 727 | ||
| 522 | cppc_check_hisi_workaround(); | 728 | cppc_check_hisi_workaround(); |
| 523 | 729 | ||
| 524 | return cpufreq_register_driver(&cppc_cpufreq_driver); | 730 | ret = cpufreq_register_driver(&cppc_cpufreq_driver); |
| 731 | if (!ret) | ||
| 732 | cppc_freq_invariance_init(); | ||
| 733 | |||
| 734 | return ret; | ||
| 525 | } | 735 | } |
| 526 | 736 | ||
| 527 | static inline void free_cpu_data(void) | 737 | static inline void free_cpu_data(void) |
| @@ -538,6 +748,7 @@ static inline void free_cpu_data(void) · 10 unmodified lines | |||
| 538 | 748 | ||
| 539 | static void __exit cppc_cpufreq_exit(void) | 749 | static void __exit cppc_cpufreq_exit(void) |
| 540 | { | 750 | { |
| 751 | cppc_freq_invariance_exit(); | ||
| 541 | cpufreq_unregister_driver(&cppc_cpufreq_driver); | 752 | cpufreq_unregister_driver(&cppc_cpufreq_driver); |
| 542 | 753 | ||
| 543 | free_cpu_data(); | 754 | free_cpu_data(); |
| @@ -255,10 +255,15 @@ static int dt_cpufreq_early_init(struct device *dev, int cpu) · 254 unmodified lines | |||
| 255 | * before updating priv->cpus. Otherwise, we will end up creating | 255 | * before updating priv->cpus. Otherwise, we will end up creating |
| 256 | * duplicate OPPs for the CPUs. | 256 | * duplicate OPPs for the CPUs. |
| 257 | * | 257 | * |
| 258 | * OPPs might be populated at runtime, don't check for error here. | 258 | * OPPs might be populated at runtime, don't fail for error here unless |
| 259 | * it is -EPROBE_DEFER. | ||
| 259 | */ | 260 | */ |
| 260 | if (!dev_pm_opp_of_cpumask_add_table(priv->cpus)) | 261 | ret = dev_pm_opp_of_cpumask_add_table(priv->cpus); |
| 262 | if (!ret) { | ||
| 261 | priv->have_static_opps = true; | 263 | priv->have_static_opps = true; |
| 264 | } else if (ret == -EPROBE_DEFER) { | ||
| 265 | goto out; | ||
| 266 | } | ||
| 262 | 267 | ||
| 263 | /* | 268 | /* |
| 264 | * The OPP table must be initialized, statically or dynamically, by this | 269 | * The OPP table must be initialized, statically or dynamically, by this |
| @@ -91,7 +91,7 @@ static DEFINE_MUTEX(set_freq_lock); · 90 unmodified lines | |||
| 91 | /* Use 800MHz when entering sleep mode */ | 91 | /* Use 800MHz when entering sleep mode */ |
| 92 | #define SLEEP_FREQ (800 * 1000) | 92 | #define SLEEP_FREQ (800 * 1000) |
| 93 | 93 | ||
| 94 | /* Tracks if cpu freqency can be updated anymore */ | 94 | /* Tracks if CPU frequency can be updated anymore */ |
| 95 | static bool no_cpufreq_access; | 95 | static bool no_cpufreq_access; |
| 96 | 96 | ||
| 97 | /* | 97 | /* |
| @@ -190,7 +190,7 @@ static u32 clkdiv_val[5][11] = { · 92 unmodified lines | |||
| 190 | 190 | ||
| 191 | /* | 191 | /* |
| 192 | * This function set DRAM refresh counter | 192 | * This function set DRAM refresh counter |
| 193 | * accoriding to operating frequency of DRAM | 193 | * according to operating frequency of DRAM |
| 194 | * ch: DMC port number 0 or 1 | 194 | * ch: DMC port number 0 or 1 |
| 195 | * freq: Operating frequency of DRAM(KHz) | 195 | * freq: Operating frequency of DRAM(KHz) |
| 196 | */ | 196 | */ |
| @@ -320,7 +320,7 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index) · 123 unmodified lines | |||
| 320 | 320 | ||
| 321 | /* | 321 | /* |
| 322 | * 3. DMC1 refresh count for 133Mhz if (index == L4) is | 322 | * 3. DMC1 refresh count for 133Mhz if (index == L4) is |
| 323 | * true refresh counter is already programed in upper | 323 | * true refresh counter is already programmed in upper |
| 324 | * code. 0x287@83Mhz | 324 | * code. 0x287@83Mhz |
| 325 | */ | 325 | */ |
| 326 | if (!bus_speed_changing) | 326 | if (!bus_speed_changing) |
| @@ -378,7 +378,7 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index) · 51 unmodified lines | |||
| 378 | /* | 378 | /* |
| 379 | * 6. Turn on APLL | 379 | * 6. Turn on APLL |
| 380 | * 6-1. Set PMS values | 380 | * 6-1. Set PMS values |
| 381 | * 6-2. Wait untile the PLL is locked | 381 | * 6-2. Wait until the PLL is locked |
| 382 | */ | 382 | */ |
| 383 | if (index == L0) | 383 | if (index == L0) |
| 384 | writel_relaxed(APLL_VAL_1000, S5P_APLL_CON); | 384 | writel_relaxed(APLL_VAL_1000, S5P_APLL_CON); |
| @@ -390,7 +390,7 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index) · 5 unmodified lines | |||
| 390 | } while (!(reg & (0x1 << 29))); | 390 | } while (!(reg & (0x1 << 29))); |
| 391 | 391 | ||
| 392 | /* | 392 | /* |
| 393 | * 7. Change souce clock from SCLKMPLL(667Mhz) | 393 | * 7. Change source clock from SCLKMPLL(667Mhz) |
| 394 | * to SCLKA2M(200Mhz) in MFC_MUX and G3D MUX | 394 | * to SCLKA2M(200Mhz) in MFC_MUX and G3D MUX |
| 395 | * (667/4=166)->(200/4=50)Mhz | 395 | * (667/4=166)->(200/4=50)Mhz |
| 396 | */ | 396 | */ |
| @@ -439,8 +439,8 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index) · 42 unmodified lines | |||
| 439 | } | 439 | } |
| 440 | 440 | ||
| 441 | /* | 441 | /* |
| 442 | * L4 level need to change memory bus speed, hence onedram clock divier | 442 | * L4 level needs to change memory bus speed, hence ONEDRAM clock |
| 443 | * and memory refresh parameter should be changed | 443 | * divider and memory refresh parameter should be changed |
| 444 | */ | 444 | */ |
| 445 | if (bus_speed_changing) { | 445 | if (bus_speed_changing) { |
| 446 | reg = readl_relaxed(S5P_CLK_DIV6); | 446 | reg = readl_relaxed(S5P_CLK_DIV6); |
| @@ -23,18 +23,31 @@ static inline unsigned long topology_get_cpu_scale(int cpu) · 22 unmodified lines | |||
| 23 | 23 | ||
| 24 | void topology_set_cpu_scale(unsigned int cpu, unsigned long capacity); | 24 | void topology_set_cpu_scale(unsigned int cpu, unsigned long capacity); |
| 25 | 25 | ||
| 26 | DECLARE_PER_CPU(unsigned long, freq_scale); | 26 | DECLARE_PER_CPU(unsigned long, arch_freq_scale); |
| 27 | 27 | ||
| 28 | static inline unsigned long topology_get_freq_scale(int cpu) | 28 | static inline unsigned long topology_get_freq_scale(int cpu) |
| 29 | { | 29 | { |
| 30 | return per_cpu(freq_scale, cpu); | 30 | return per_cpu(arch_freq_scale, cpu); |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | void topology_set_freq_scale(const struct cpumask *cpus, unsigned long cur_freq, | 33 | void topology_set_freq_scale(const struct cpumask *cpus, unsigned long cur_freq, |
| 34 | unsigned long max_freq); | 34 | unsigned long max_freq); |
| 35 | bool topology_scale_freq_invariant(void); | 35 | bool topology_scale_freq_invariant(void); |
| 36 | 36 | ||
| 37 | bool arch_freq_counters_available(const struct cpumask *cpus); | 37 | enum scale_freq_source { |
| 38 | SCALE_FREQ_SOURCE_CPUFREQ = 0, | ||
| 39 | SCALE_FREQ_SOURCE_ARCH, | ||
| 40 | SCALE_FREQ_SOURCE_CPPC, | ||
| 41 | }; | ||
| 42 | |||
| 43 | struct scale_freq_data { | ||
| 44 | enum scale_freq_source source; | ||
| 45 | void (*set_freq_scale)(void); | ||
| 46 | }; | ||
| 47 | |||
| 48 | void topology_scale_freq_tick(void); | ||
| 49 | void topology_set_scale_freq_source(struct scale_freq_data *data, const struct cpumask *cpus); | ||
| 50 | void topology_clear_scale_freq_source(enum scale_freq_source source, const struct cpumask *cpus); | ||
| 38 | 51 | ||
| 39 | DECLARE_PER_CPU(unsigned long, thermal_pressure); | 52 | DECLARE_PER_CPU(unsigned long, thermal_pressure); |
| 40 | 53 | ||
| @@ -6384,6 +6384,7 @@ int sched_setattr_nocheck(struct task_struct *p, const struct sched_attr *attr) · 6383 unmodified lines | |||
| 6384 | { | 6384 | { |
| 6385 | return __sched_setscheduler(p, attr, false, true); | 6385 | return __sched_setscheduler(p, attr, false, true); |
| 6386 | } | 6386 | } |
| 6387 | EXPORT_SYMBOL_GPL(sched_setattr_nocheck); | ||
| 6387 | 6388 | ||
| 6388 | /** | 6389 | /** |
| 6389 | * sched_setscheduler_nocheck - change the scheduling policy and/or RT priority of a thread from kernelspace. | 6390 | * sched_setscheduler_nocheck - change the scheduling policy and/or RT priority of a thread from kernelspace. |