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 | 17 | #include <linux/arch_topology.h> |
| 18 | 18 | |
| 19 | 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 | 21 | /* Replace task scheduler's default frequency-invariant accounting */ |
| 22 | +#define arch_scale_freq_tick topology_scale_freq_tick | |
| 31 | 23 | #define arch_set_freq_scale topology_set_freq_scale |
| 32 | 24 | #define arch_scale_freq_capacity topology_get_freq_scale |
| 33 | 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 | 199 | return 0; |
| 200 | 200 | } |
| 201 | 201 | |
| 202 | −static DEFINE_STATIC_KEY_FALSE(amu_fie_key); | |
| 203 | −#define amu_freq_invariant() static_branch_unlikely(&amu_fie_key) | |
| 202 | +static void amu_scale_freq_tick(void) | |
| 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 | 241 | static void amu_fie_setup(const struct cpumask *cpus) |
| 206 | 242 | { |
| 207 | − bool invariant; | |
| 208 | 243 | int cpu; |
| 209 | 244 | |
| 210 | 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 | 257 | cpumask_or(amu_fie_cpus, amu_fie_cpus, cpus); |
| 223 | 258 | |
| 224 | − invariant = topology_scale_freq_invariant(); | |
| 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); | |
| 259 | + topology_set_scale_freq_source(&amu_sfd, amu_fie_cpus); | |
| 231 | 260 | |
| 232 | 261 | pr_debug("CPUs[%*pbl]: counters will be used for FIE.", |
| 233 | 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 | 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 | 276 | * initialized AMU support and enabled invariance. The AMU counters will |
| 257 | 277 | * keep on working just fine in the absence of the cpufreq driver, and |
| 258 | 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 | |
| 260 | − * CPUs are running at. | |
| 279 | + * value of arch_freq_scale will remain valid as that is the frequency | |
| 280 | + * those CPUs are running at. | |
| 261 | 281 | */ |
| 262 | 282 | |
| 263 | 283 | return 0; |
| @@ -283,53 +303,6 @@ static int __init init_amu_fie(void) · 19 unmodified lines | ||
| 283 | 303 | } |
| 284 | 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 | 306 | #ifdef CONFIG_ACPI_CPPC_LIB |
| 334 | 307 | #include <acpi/cppc_acpi.h> |
| 335 | 308 | |
| @@ -21,17 +21,94 @@ · 20 unmodified lines | ||
| 21 | 21 | #include <linux/sched.h> |
| 22 | 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 | 33 | bool topology_scale_freq_invariant(void) |
| 25 | 34 | { |
| 26 | 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 | 113 | void topology_set_freq_scale(const struct cpumask *cpus, unsigned long cur_freq, |
| 37 | 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 | 124 | * want to update the scale factor with information from CPUFREQ. |
| 48 | 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 | 128 | return; |
| 52 | 129 | |
| 53 | 130 | scale = (cur_freq << SCHED_CAPACITY_SHIFT) / max_freq; |
| 54 | 131 | |
| 55 | 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 | 136 | DEFINE_PER_CPU(unsigned long, cpu_scale) = SCHED_CAPACITY_SCALE; |
| @@ -84,6 +84,7 @@ struct clk_pm_cpu { · 83 unmodified lines | ||
| 84 | 84 | void __iomem *reg_div; |
| 85 | 85 | u8 shift_div; |
| 86 | 86 | struct regmap *nb_pm_base; |
| 87 | + unsigned long l1_expiration; | |
| 87 | 88 | }; |
| 88 | 89 | |
| 89 | 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 | 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 | 444 | static unsigned long clk_pm_cpu_recalc_rate(struct clk_hw *hw, |
| 471 | 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 | |
| 518 | − * respectively) to L0 frequency (1.2 Ghz) requires a significant | |
| 491 | + * Workaround when base CPU frequnecy is 1000 or 1200 MHz | |
| 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 | 495 | * amount of time to let VDD stabilize to the appropriate |
| 520 | 496 | * voltage. This amount of time is large enough that it cannot be |
| 521 | 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 | 501 | * To work around this problem, we prevent switching directly from the |
| 526 | 502 | * L2/L3 frequencies to the L0 frequency, and instead switch to the L1 |
| 527 | 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 | 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 | 512 | unsigned int cur_level; |
| 535 | 513 | |
| 536 | − if (rate != 1200 * 1000 * 1000) | |
| 537 | − return; | |
| 538 | − | |
| 539 | 514 | regmap_read(base, ARMADA_37XX_NB_CPU_LOAD, &cur_level); |
| 540 | 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 | 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 | 547 | regmap_update_bits(base, ARMADA_37XX_NB_CPU_LOAD, |
| 545 | 548 | ARMADA_37XX_NB_CPU_LOAD_MASK, |
| 546 | 549 | ARMADA_37XX_DVFS_LOAD_1); |
| 547 | 550 | msleep(20); |
| 551 | + | |
| 552 | +invalidate_l1_exp: | |
| 553 | + pm_cpu->l1_expiration = 0; | |
| 548 | 554 | } |
| 549 | 555 | |
| 550 | 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 | 584 | reg = ARMADA_37XX_NB_CPU_LOAD; |
| 579 | 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 | 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 | 601 | static const struct clk_ops clk_pm_cpu_ops = { |
| 594 | 602 | .get_parent = clk_pm_cpu_get_parent, |
| 595 | − .set_parent = clk_pm_cpu_set_parent, | |
| 596 | 603 | .round_rate = clk_pm_cpu_round_rate, |
| 597 | 604 | .set_rate = clk_pm_cpu_set_rate, |
| 598 | 605 | .recalc_rate = clk_pm_cpu_recalc_rate, |
| @@ -19,6 +19,16 @@ config ACPI_CPPC_CPUFREQ · 18 unmodified lines | ||
| 19 | 19 | |
| 20 | 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 | 32 | config ARM_ALLWINNER_SUN50I_CPUFREQ_NVMEM |
| 23 | 33 | tristate "Allwinner nvmem based SUN50I CPUFreq driver" |
| 24 | 34 | depends on ARCH_SUNXI |
| @@ -25,6 +25,10 @@ · 24 unmodified lines | ||
| 25 | 25 | |
| 26 | 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 | 32 | /* Power management in North Bridge register set */ |
| 29 | 33 | #define ARMADA_37XX_NB_L0L1 0x18 |
| 30 | 34 | #define ARMADA_37XX_NB_L2L3 0x1C |
| @@ -69,6 +73,8 @@ · 38 unmodified lines | ||
| 69 | 73 | #define LOAD_LEVEL_NR 4 |
| 70 | 74 | |
| 71 | 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 | 79 | /* AVS value for the corresponding voltage (in mV) */ |
| 74 | 80 | static int avs_map[] = { |
| @@ -80,6 +86,8 @@ static int avs_map[] = { · 5 unmodified lines | ||
| 80 | 86 | }; |
| 81 | 87 | |
| 82 | 88 | struct armada37xx_cpufreq_state { |
| 89 | + struct platform_device *pdev; | |
| 90 | + struct device *cpu_dev; | |
| 83 | 91 | struct regmap *regmap; |
| 84 | 92 | u32 nb_l0l1; |
| 85 | 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 | 128 | * will be configured then the DVFS will be enabled. |
| 121 | 129 | */ |
| 122 | 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 | 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 | 141 | for (load_lvl = 0; load_lvl < LOAD_LEVEL_NR; load_lvl++) { |
| 129 | 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 | 155 | mask = (ARMADA_37XX_NB_CLK_SEL_MASK |
| 143 | 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 | 164 | * Set cpu divider based on the pre-computed array in |
| 147 | 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 | 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 | 212 | * - L2 & L3 voltage should be about 150mv smaller than L0 voltage. |
| 203 | 213 | * This function calculates L1 & L2 & L3 AVS values dynamically based |
| 204 | 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 | 218 | static void __init armada37xx_cpufreq_avs_configure(struct regmap *base, |
| 207 | 219 | struct armada_37xx_dvfs *dvfs) |
| @@ -233,6 +245,19 @@ static void __init armada37xx_cpufreq_avs_configure(struct regmap *base, · 25 unmodified lines | ||
| 233 | 245 | for (load_level = 1; load_level < LOAD_LEVEL_NR; load_level++) |
| 234 | 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 | 261 | return; |
| 237 | 262 | } |
| 238 | 263 | |
| @@ -252,6 +277,26 @@ static void __init armada37xx_cpufreq_avs_configure(struct regmap *base, · 13 unmodified lines | ||
| 252 | 277 | target_vm = avs_map[l0_vdd_min] - 150; |
| 253 | 278 | target_vm = target_vm > MIN_VOLT_MV ? target_vm : MIN_VOLT_MV; |
| 254 | 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 | 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 | 402 | struct armada_37xx_dvfs *dvfs; |
| 358 | 403 | struct platform_device *pdev; |
| 359 | 404 | unsigned long freq; |
| 360 | − unsigned int cur_frequency, base_frequency; | |
| 361 | − struct regmap *nb_pm_base, *avs_base; | |
| 405 | + unsigned int base_frequency; | |
| 406 | + struct regmap *nb_clk_base, *nb_pm_base, *avs_base; | |
| 362 | 407 | struct device *cpu_dev; |
| 363 | 408 | int load_lvl, ret; |
| 364 | 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 | 416 | nb_pm_base = |
| 367 | 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 | 463 | return -EINVAL; |
| 414 | 464 | } |
| 415 | 465 | |
| 416 | − /* Get nominal (current) CPU 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); | |
| 466 | + dvfs = armada_37xx_cpu_freq_info_get(base_frequency); | |
| 425 | 467 | if (!dvfs) { |
| 426 | 468 | clk_put(clk); |
| 427 | 469 | return -EINVAL; |
| @@ -439,7 +481,7 @@ static int __init armada37xx_cpufreq_driver_init(void) · 11 unmodified lines | ||
| 439 | 481 | armada37xx_cpufreq_avs_configure(avs_base, dvfs); |
| 440 | 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 | 485 | clk_put(clk); |
| 444 | 486 | |
| 445 | 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 | 508 | if (ret) |
| 467 | 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 | 514 | return 0; |
| 470 | 515 | |
| 471 | 516 | disable_dvfs: |
| @@ -473,7 +518,7 @@ disable_dvfs: · 1 unmodified line | ||
| 473 | 518 | remove_opp: |
| 474 | 519 | /* clean-up the already added opp before leaving */ |
| 475 | 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 | 522 | dev_pm_opp_remove(cpu_dev, freq); |
| 478 | 523 | } |
| 479 | 524 | |
| @@ -484,6 +529,26 @@ remove_opp: · 4 unmodified lines | ||
| 484 | 529 | /* late_initcall, to guarantee the driver is loaded after A37xx clock driver */ |
| 485 | 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 | 552 | static const struct of_device_id __maybe_unused armada37xx_cpufreq_of_match[] = { |
| 488 | 553 | { .compatible = "marvell,armada-3700-nb-pm" }, |
| 489 | 554 | { }, |
| @@ -10,14 +10,18 @@ · 9 unmodified lines | ||
| 10 | 10 | |
| 11 | 11 | #define pr_fmt(fmt) "CPPC Cpufreq:" fmt |
| 12 | 12 | |
| 13 | +#include <linux/arch_topology.h> | |
| 13 | 14 | #include <linux/kernel.h> |
| 14 | 15 | #include <linux/module.h> |
| 15 | 16 | #include <linux/delay.h> |
| 16 | 17 | #include <linux/cpu.h> |
| 17 | 18 | #include <linux/cpufreq.h> |
| 18 | 19 | #include <linux/dmi.h> |
| 20 | +#include <linux/irq_work.h> | |
| 21 | +#include <linux/kthread.h> | |
| 19 | 22 | #include <linux/time.h> |
| 20 | 23 | #include <linux/vmalloc.h> |
| 24 | +#include <uapi/linux/sched/types.h> | |
| 21 | 25 | |
| 22 | 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 | 262 | /* Callback function used to retrieve the max frequency from DMI */ |
| 61 | 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 | 419 | unsigned long implementor = read_cpuid_implementor(); |
| 218 | 420 | unsigned long part_num = read_cpuid_part_number(); |
| 219 | − unsigned int delay_us = 0; | |
| 220 | 421 | |
| 221 | 422 | switch (implementor) { |
| 222 | 423 | case ARM_CPU_IMP_QCOM: |
| 223 | 424 | switch (part_num) { |
| 224 | 425 | case QCOM_CPU_PART_FALKOR_V1: |
| 225 | 426 | case QCOM_CPU_PART_FALKOR: |
| 226 | − delay_us = 10000; | |
| 227 | − break; | |
| 228 | − default: | |
| 229 | − delay_us = cppc_get_transition_latency(cpu) / NSEC_PER_USEC; | |
| 230 | − break; | |
| 427 | + return 10000; | |
| 231 | 428 | } |
| 232 | − break; | |
| 233 | − default: | |
| 234 | − delay_us = cppc_get_transition_latency(cpu) / NSEC_PER_USEC; | |
| 235 | − break; | |
| 236 | 429 | } |
| 237 | − | |
| 238 | − return delay_us; | |
| 430 | + return cppc_get_transition_latency(cpu) / NSEC_PER_USEC; | |
| 239 | 431 | } |
| 240 | 432 | |
| 241 | 433 | #else |
| @@ -355,9 +547,12 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) · 113 unmodified lines | ||
| 355 | 547 | cpu_data->perf_ctrls.desired_perf = caps->highest_perf; |
| 356 | 548 | |
| 357 | 549 | ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls); |
| 358 | − if (ret) | |
| 550 | + if (ret) { | |
| 359 | 551 | pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n", |
| 360 | 552 | caps->highest_perf, cpu, ret); |
| 553 | + } else { | |
| 554 | + cppc_freq_invariance_policy_init(policy, cpu_data); | |
| 555 | + } | |
| 361 | 556 | |
| 362 | 557 | return ret; |
| 363 | 558 | } |
| @@ -370,12 +565,12 @@ static inline u64 get_delta(u64 t1, u64 t0) · 6 unmodified lines | ||
| 370 | 565 | return (u32)t1 - (u32)t0; |
| 371 | 566 | } |
| 372 | 567 | |
| 373 | −static int cppc_get_rate_from_fbctrs(struct cppc_cpudata *cpu_data, | |
| 374 | − struct cppc_perf_fb_ctrs fb_ctrs_t0, | |
| 375 | − struct cppc_perf_fb_ctrs fb_ctrs_t1) | |
| 568 | +static int cppc_perf_from_fbctrs(struct cppc_cpudata *cpu_data, | |
| 569 | + struct cppc_perf_fb_ctrs fb_ctrs_t0, | |
| 570 | + struct cppc_perf_fb_ctrs fb_ctrs_t1) | |
| 376 | 571 | { |
| 377 | 572 | u64 delta_reference, delta_delivered; |
| 378 | − u64 reference_perf, delivered_perf; | |
| 573 | + u64 reference_perf; | |
| 379 | 574 | |
| 380 | 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 | 579 | delta_delivered = get_delta(fb_ctrs_t1.delivered, |
| 385 | 580 | fb_ctrs_t0.delivered); |
| 386 | 581 | |
| 387 | − /* Check to avoid divide-by zero */ | |
| 388 | − if (delta_reference || delta_delivered) | |
| 389 | − delivered_perf = (reference_perf * delta_delivered) / | |
| 390 | − delta_reference; | |
| 391 | − else | |
| 392 | − delivered_perf = cpu_data->perf_ctrls.desired_perf; | |
| 582 | + /* Check to avoid divide-by zero and invalid delivered_perf */ | |
| 583 | + if (!delta_reference || !delta_delivered) | |
| 584 | + return cpu_data->perf_ctrls.desired_perf; | |
| 585 | + | |
| 586 | + return (reference_perf * delta_delivered) / delta_reference; | |
| 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 | 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 | 719 | static int __init cppc_cpufreq_init(void) |
| 516 | 720 | { |
| 721 | + int ret; | |
| 722 | + | |
| 517 | 723 | if ((acpi_disabled) || !acpi_cpc_valid()) |
| 518 | 724 | return -ENODEV; |
| 519 | 725 | |
| @@ -521,7 +727,11 @@ static int __init cppc_cpufreq_init(void) · 1 unmodified line | ||
| 521 | 727 | |
| 522 | 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 | 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 | 749 | static void __exit cppc_cpufreq_exit(void) |
| 540 | 750 | { |
| 751 | + cppc_freq_invariance_exit(); | |
| 541 | 752 | cpufreq_unregister_driver(&cppc_cpufreq_driver); |
| 542 | 753 | |
| 543 | 754 | free_cpu_data(); |
| @@ -255,10 +255,15 @@ static int dt_cpufreq_early_init(struct device *dev, int cpu) · 254 unmodified lines | ||
| 255 | 255 | * before updating priv->cpus. Otherwise, we will end up creating |
| 256 | 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 | 263 | priv->have_static_opps = true; |
| 264 | + } else if (ret == -EPROBE_DEFER) { | |
| 265 | + goto out; | |
| 266 | + } | |
| 262 | 267 | |
| 263 | 268 | /* |
| 264 | 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 | 91 | /* Use 800MHz when entering sleep mode */ |
| 92 | 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 | 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 | 192 | * This function set DRAM refresh counter |
| 193 | − * accoriding to operating frequency of DRAM | |
| 193 | + * according to operating frequency of DRAM | |
| 194 | 194 | * ch: DMC port number 0 or 1 |
| 195 | 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 | 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 | 324 | * code. 0x287@83Mhz |
| 325 | 325 | */ |
| 326 | 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 | 379 | * 6. Turn on APLL |
| 380 | 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 | 383 | if (index == L0) |
| 384 | 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 | 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 | 394 | * to SCLKA2M(200Mhz) in MFC_MUX and G3D MUX |
| 395 | 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 | |
| 443 | − * and memory refresh parameter should be changed | |
| 442 | + * L4 level needs to change memory bus speed, hence ONEDRAM clock | |
| 443 | + * divider and memory refresh parameter should be changed | |
| 444 | 444 | */ |
| 445 | 445 | if (bus_speed_changing) { |
| 446 | 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 | 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 | 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 | 33 | void topology_set_freq_scale(const struct cpumask *cpus, unsigned long cur_freq, |
| 34 | 34 | unsigned long max_freq); |
| 35 | 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 | 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 | 6385 | return __sched_setscheduler(p, attr, false, true); |
| 6386 | 6386 | } |
| 6387 | +EXPORT_SYMBOL_GPL(sched_setattr_nocheck); | |
| 6387 | 6388 | |
| 6388 | 6389 | /** |
| 6389 | 6390 | * sched_setscheduler_nocheck - change the scheduling policy and/or RT priority of a thread from kernelspace. |