Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'pm-6.13-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull more power management updates from Rafael Wysocki:
"These mostly are updates of cpufreq drivers used on ARM platforms plus
one new DT-based cpufreq driver for virtualized guests and two cpuidle
changes that should not make any difference on systems currently in
the field, but will be needed for future development:

- Add virtual cpufreq driver for guest kernels (David Dai)

- Minor cleanup to various cpufreq drivers (Andy Shevchenko, Dhruva
Gole, Jie Zhan, Jinjie Ruan, Shuosheng Huang, Sibi Sankar, and Yuan
Can)

- Revert "cpufreq: brcmstb-avs-cpufreq: Fix initial command check"
(Colin Ian King)

- Improve DT bindings for qcom-hw driver (Dmitry Baryshkov, Konrad
Dybcio, and Nikunj Kela)

- Make cpuidle_play_dead() try all idle states with :enter_dead()
callbacks and change their return type to void (Rafael Wysocki)"

* tag 'pm-6.13-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (22 commits)
cpuidle: Change :enter_dead() driver callback return type to void
cpuidle: Do not return from cpuidle_play_dead() on callback failures
arm64: dts: qcom: sc8180x: Add a SoC-specific compatible to cpufreq-hw
dt-bindings: cpufreq: cpufreq-qcom-hw: Add SC8180X compatible
cpufreq: sun50i: add a100 cpufreq support
cpufreq: mediatek-hw: Fix wrong return value in mtk_cpufreq_get_cpu_power()
cpufreq: CPPC: Fix wrong return value in cppc_get_cpu_power()
cpufreq: CPPC: Fix wrong return value in cppc_get_cpu_cost()
cpufreq: loongson3: Check for error code from devm_mutex_init() call
cpufreq: scmi: Fix cleanup path when boost enablement fails
cpufreq: CPPC: Fix possible null-ptr-deref for cppc_get_cpu_cost()
cpufreq: CPPC: Fix possible null-ptr-deref for cpufreq_cpu_get_raw()
Revert "cpufreq: brcmstb-avs-cpufreq: Fix initial command check"
dt-bindings: cpufreq: cpufreq-qcom-hw: Add SAR2130P compatible
cpufreq: add virtual-cpufreq driver
dt-bindings: cpufreq: add virtual cpufreq device
cpufreq: loongson2: Unregister platform_driver on failure
cpufreq: ti-cpufreq: Remove revision offsets in AM62 family
cpufreq: ti-cpufreq: Allow backward compatibility for efuse syscon
cppc_cpufreq: Remove HiSilicon CPPC workaround
...

+517 -103
+6
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml
··· 23 23 - enum: 24 24 - qcom,qcm2290-cpufreq-hw 25 25 - qcom,sc7180-cpufreq-hw 26 + - qcom,sc8180x-cpufreq-hw 26 27 - qcom,sdm670-cpufreq-hw 27 28 - qcom,sdm845-cpufreq-hw 28 29 - qcom,sm6115-cpufreq-hw ··· 35 34 items: 36 35 - enum: 37 36 - qcom,qdu1000-cpufreq-epss 37 + - qcom,sa8255p-cpufreq-epss 38 38 - qcom,sa8775p-cpufreq-epss 39 + - qcom,sar2130p-cpufreq-epss 39 40 - qcom,sc7280-cpufreq-epss 40 41 - qcom,sc8280xp-cpufreq-epss 41 42 - qcom,sdx75-cpufreq-epss ··· 110 107 contains: 111 108 enum: 112 109 - qcom,qcm2290-cpufreq-hw 110 + - qcom,sar2130p-cpufreq-epss 113 111 then: 114 112 properties: 115 113 reg: ··· 134 130 contains: 135 131 enum: 136 132 - qcom,qdu1000-cpufreq-epss 133 + - qcom,sa8255p-cpufreq-epss 137 134 - qcom,sc7180-cpufreq-hw 135 + - qcom,sc8180x-cpufreq-hw 138 136 - qcom,sc8280xp-cpufreq-epss 139 137 - qcom,sdm670-cpufreq-hw 140 138 - qcom,sdm845-cpufreq-hw
+48
Documentation/devicetree/bindings/cpufreq/qemu,virtual-cpufreq.yaml
··· 1 + # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/cpufreq/qemu,virtual-cpufreq.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: Virtual CPUFreq 8 + 9 + maintainers: 10 + - David Dai <davidai@google.com> 11 + - Saravana Kannan <saravanak@google.com> 12 + 13 + description: 14 + Virtual CPUFreq is a virtualized driver in guest kernels that sends performance 15 + selection of its vCPUs as a hint to the host through MMIO regions. Each vCPU 16 + is associated with a performance domain which can be shared with other vCPUs. 17 + Each performance domain has its own set of registers for performance controls. 18 + 19 + properties: 20 + compatible: 21 + const: qemu,virtual-cpufreq 22 + 23 + reg: 24 + maxItems: 1 25 + description: 26 + Address and size of region containing performance controls for each of the 27 + performance domains. Regions for each performance domain is placed 28 + contiguously and contain registers for controlling DVFS(Dynamic Frequency 29 + and Voltage) characteristics. The size of the region is proportional to 30 + total number of performance domains. 31 + 32 + required: 33 + - compatible 34 + - reg 35 + 36 + additionalProperties: false 37 + 38 + examples: 39 + - | 40 + soc { 41 + #address-cells = <1>; 42 + #size-cells = <1>; 43 + 44 + cpufreq@1040000 { 45 + compatible = "qemu,virtual-cpufreq"; 46 + reg = <0x1040000 0x2000>; 47 + }; 48 + };
+1 -1
arch/arm64/boot/dts/qcom/sc8180x.dtsi
··· 3889 3889 }; 3890 3890 3891 3891 cpufreq_hw: cpufreq@18323000 { 3892 - compatible = "qcom,cpufreq-hw"; 3892 + compatible = "qcom,sc8180x-cpufreq-hw", "qcom,cpufreq-hw"; 3893 3893 reg = <0 0x18323000 0 0x1400>, <0 0x18325800 0 0x1400>; 3894 3894 reg-names = "freq-domain0", "freq-domain1"; 3895 3895
+2 -5
drivers/acpi/processor_idle.c
··· 578 578 * @dev: the target CPU 579 579 * @index: the index of suggested state 580 580 */ 581 - static int acpi_idle_play_dead(struct cpuidle_device *dev, int index) 581 + static void acpi_idle_play_dead(struct cpuidle_device *dev, int index) 582 582 { 583 583 struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu); 584 584 ··· 591 591 else if (cx->entry_method == ACPI_CSTATE_SYSTEMIO) { 592 592 io_idle(cx->address); 593 593 } else 594 - return -ENODEV; 594 + return; 595 595 } 596 - 597 - /* Never reached */ 598 - return 0; 599 596 } 600 597 601 598 static __always_inline bool acpi_idle_fallback_to_c1(struct acpi_processor *pr)
+14
drivers/cpufreq/Kconfig
··· 217 217 218 218 If in doubt, say N. 219 219 220 + config CPUFREQ_VIRT 221 + tristate "Virtual cpufreq driver" 222 + depends on GENERIC_ARCH_TOPOLOGY 223 + help 224 + This adds a virtualized cpufreq driver for guest kernels that 225 + read/writes to a MMIO region for a virtualized cpufreq device to 226 + communicate with the host. It sends performance requests to the host 227 + which gets used as a hint to schedule vCPU threads and select CPU 228 + frequency. If a VM does not support a virtualized FIE such as AMUs, 229 + it updates the frequency scaling factor by polling host CPU frequency 230 + to enable accurate Per-Entity Load Tracking for tasks running in the guest. 231 + 232 + If in doubt, say N. 233 + 220 234 config CPUFREQ_DT_PLATDEV 221 235 tristate "Generic DT based cpufreq platdev driver" 222 236 depends on OF
+1
drivers/cpufreq/Makefile
··· 16 16 17 17 obj-$(CONFIG_CPUFREQ_DT) += cpufreq-dt.o 18 18 obj-$(CONFIG_CPUFREQ_DT_PLATDEV) += cpufreq-dt-platdev.o 19 + obj-$(CONFIG_CPUFREQ_VIRT) += virtual-cpufreq.o 19 20 20 21 # Traces 21 22 CFLAGS_amd-pstate-trace.o := -I$(src)
+2 -2
drivers/cpufreq/brcmstb-avs-cpufreq.c
··· 474 474 rc = brcm_avs_get_pmap(priv, NULL); 475 475 magic = readl(priv->base + AVS_MBOX_MAGIC); 476 476 477 - return (magic == AVS_FIRMWARE_MAGIC) && ((rc != -ENOTSUPP) || 478 - (rc != -EINVAL)); 477 + return (magic == AVS_FIRMWARE_MAGIC) && (rc != -ENOTSUPP) && 478 + (rc != -EINVAL); 479 479 } 480 480 481 481 static unsigned int brcm_avs_cpufreq_get(unsigned int cpu)
+53 -83
drivers/cpufreq/cppc_cpufreq.c
··· 36 36 37 37 static bool boost_supported; 38 38 39 - struct cppc_workaround_oem_info { 40 - char oem_id[ACPI_OEM_ID_SIZE + 1]; 41 - char oem_table_id[ACPI_OEM_TABLE_ID_SIZE + 1]; 42 - u32 oem_revision; 43 - }; 44 - 45 - static struct cppc_workaround_oem_info wa_info[] = { 46 - { 47 - .oem_id = "HISI ", 48 - .oem_table_id = "HIP07 ", 49 - .oem_revision = 0, 50 - }, { 51 - .oem_id = "HISI ", 52 - .oem_table_id = "HIP08 ", 53 - .oem_revision = 0, 54 - } 55 - }; 56 - 57 39 static struct cpufreq_driver cppc_cpufreq_driver; 58 40 41 + #ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE 59 42 static enum { 60 43 FIE_UNSET = -1, 61 44 FIE_ENABLED, 62 45 FIE_DISABLED 63 46 } fie_disabled = FIE_UNSET; 64 47 65 - #ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE 66 48 module_param(fie_disabled, int, 0444); 67 49 MODULE_PARM_DESC(fie_disabled, "Disable Frequency Invariance Engine (FIE)"); 68 50 ··· 60 78 static DEFINE_PER_CPU(struct cppc_freq_invariance, cppc_freq_inv); 61 79 static struct kthread_worker *kworker_fie; 62 80 63 - static unsigned int hisi_cppc_cpufreq_get_rate(unsigned int cpu); 64 81 static int cppc_perf_from_fbctrs(struct cppc_cpudata *cpu_data, 65 82 struct cppc_perf_fb_ctrs *fb_ctrs_t0, 66 83 struct cppc_perf_fb_ctrs *fb_ctrs_t1); ··· 99 118 100 119 perf = cppc_perf_from_fbctrs(cpu_data, &cppc_fi->prev_perf_fb_ctrs, 101 120 &fb_ctrs); 121 + if (!perf) 122 + return; 123 + 102 124 cppc_fi->prev_perf_fb_ctrs = fb_ctrs; 103 125 104 126 perf <<= SCHED_CAPACITY_SHIFT; ··· 404 420 struct cppc_cpudata *cpu_data; 405 421 406 422 policy = cpufreq_cpu_get_raw(cpu_dev->id); 423 + if (!policy) 424 + return -EINVAL; 425 + 407 426 cpu_data = policy->driver_data; 408 427 perf_caps = &cpu_data->perf_caps; 409 428 max_cap = arch_scale_cpu_capacity(cpu_dev->id); ··· 474 487 int step; 475 488 476 489 policy = cpufreq_cpu_get_raw(cpu_dev->id); 490 + if (!policy) 491 + return -EINVAL; 492 + 477 493 cpu_data = policy->driver_data; 478 494 perf_caps = &cpu_data->perf_caps; 479 495 max_cap = arch_scale_cpu_capacity(cpu_dev->id); ··· 714 724 delta_delivered = get_delta(fb_ctrs_t1->delivered, 715 725 fb_ctrs_t0->delivered); 716 726 717 - /* Check to avoid divide-by zero and invalid delivered_perf */ 727 + /* 728 + * Avoid divide-by zero and unchanged feedback counters. 729 + * Leave it for callers to handle. 730 + */ 718 731 if (!delta_reference || !delta_delivered) 719 - return cpu_data->perf_ctrls.desired_perf; 732 + return 0; 720 733 721 734 return (reference_perf * delta_delivered) / delta_reference; 735 + } 736 + 737 + static int cppc_get_perf_ctrs_sample(int cpu, 738 + struct cppc_perf_fb_ctrs *fb_ctrs_t0, 739 + struct cppc_perf_fb_ctrs *fb_ctrs_t1) 740 + { 741 + int ret; 742 + 743 + ret = cppc_get_perf_ctrs(cpu, fb_ctrs_t0); 744 + if (ret) 745 + return ret; 746 + 747 + udelay(2); /* 2usec delay between sampling */ 748 + 749 + return cppc_get_perf_ctrs(cpu, fb_ctrs_t1); 722 750 } 723 751 724 752 static unsigned int cppc_cpufreq_get_rate(unsigned int cpu) ··· 754 746 755 747 cpufreq_cpu_put(policy); 756 748 757 - ret = cppc_get_perf_ctrs(cpu, &fb_ctrs_t0); 758 - if (ret) 759 - return 0; 760 - 761 - udelay(2); /* 2usec delay between sampling */ 762 - 763 - ret = cppc_get_perf_ctrs(cpu, &fb_ctrs_t1); 764 - if (ret) 765 - return 0; 749 + ret = cppc_get_perf_ctrs_sample(cpu, &fb_ctrs_t0, &fb_ctrs_t1); 750 + if (ret) { 751 + if (ret == -EFAULT) 752 + /* Any of the associated CPPC regs is 0. */ 753 + goto out_invalid_counters; 754 + else 755 + return 0; 756 + } 766 757 767 758 delivered_perf = cppc_perf_from_fbctrs(cpu_data, &fb_ctrs_t0, 768 759 &fb_ctrs_t1); 760 + if (!delivered_perf) 761 + goto out_invalid_counters; 762 + 763 + return cppc_perf_to_khz(&cpu_data->perf_caps, delivered_perf); 764 + 765 + out_invalid_counters: 766 + /* 767 + * Feedback counters could be unchanged or 0 when a cpu enters a 768 + * low-power idle state, e.g. clock-gated or power-gated. 769 + * Use desired perf for reflecting frequency. Get the latest register 770 + * value first as some platforms may update the actual delivered perf 771 + * there; if failed, resort to the cached desired perf. 772 + */ 773 + if (cppc_get_desired_perf(cpu, &delivered_perf)) 774 + delivered_perf = cpu_data->perf_ctrls.desired_perf; 769 775 770 776 return cppc_perf_to_khz(&cpu_data->perf_caps, delivered_perf); 771 777 } ··· 834 812 .name = "cppc_cpufreq", 835 813 }; 836 814 837 - /* 838 - * HISI platform does not support delivered performance counter and 839 - * reference performance counter. It can calculate the performance using the 840 - * platform specific mechanism. We reuse the desired performance register to 841 - * store the real performance calculated by the platform. 842 - */ 843 - static unsigned int hisi_cppc_cpufreq_get_rate(unsigned int cpu) 844 - { 845 - struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); 846 - struct cppc_cpudata *cpu_data; 847 - u64 desired_perf; 848 - int ret; 849 - 850 - if (!policy) 851 - return -ENODEV; 852 - 853 - cpu_data = policy->driver_data; 854 - 855 - cpufreq_cpu_put(policy); 856 - 857 - ret = cppc_get_desired_perf(cpu, &desired_perf); 858 - if (ret < 0) 859 - return -EIO; 860 - 861 - return cppc_perf_to_khz(&cpu_data->perf_caps, desired_perf); 862 - } 863 - 864 - static void cppc_check_hisi_workaround(void) 865 - { 866 - struct acpi_table_header *tbl; 867 - acpi_status status = AE_OK; 868 - int i; 869 - 870 - status = acpi_get_table(ACPI_SIG_PCCT, 0, &tbl); 871 - if (ACPI_FAILURE(status) || !tbl) 872 - return; 873 - 874 - for (i = 0; i < ARRAY_SIZE(wa_info); i++) { 875 - if (!memcmp(wa_info[i].oem_id, tbl->oem_id, ACPI_OEM_ID_SIZE) && 876 - !memcmp(wa_info[i].oem_table_id, tbl->oem_table_id, ACPI_OEM_TABLE_ID_SIZE) && 877 - wa_info[i].oem_revision == tbl->oem_revision) { 878 - /* Overwrite the get() callback */ 879 - cppc_cpufreq_driver.get = hisi_cppc_cpufreq_get_rate; 880 - fie_disabled = FIE_DISABLED; 881 - break; 882 - } 883 - } 884 - 885 - acpi_put_table(tbl); 886 - } 887 - 888 815 static int __init cppc_cpufreq_init(void) 889 816 { 890 817 int ret; ··· 841 870 if (!acpi_cpc_valid()) 842 871 return -ENODEV; 843 872 844 - cppc_check_hisi_workaround(); 845 873 cppc_freq_invariance_init(); 846 874 populate_efficiency_class(); 847 875
+1
drivers/cpufreq/cpufreq-dt-platdev.c
··· 103 103 * platforms using "operating-points-v2" property. 104 104 */ 105 105 static const struct of_device_id blocklist[] __initconst = { 106 + { .compatible = "allwinner,sun50i-a100" }, 106 107 { .compatible = "allwinner,sun50i-h6", }, 107 108 { .compatible = "allwinner,sun50i-h616", }, 108 109 { .compatible = "allwinner,sun50i-h618", },
+3 -1
drivers/cpufreq/loongson2_cpufreq.c
··· 148 148 149 149 ret = cpufreq_register_driver(&loongson2_cpufreq_driver); 150 150 151 - if (!ret && !nowait) { 151 + if (ret) { 152 + platform_driver_unregister(&platform_driver); 153 + } else if (!nowait) { 152 154 saved_cpu_wait = cpu_wait; 153 155 cpu_wait = loongson2_cpu_wait; 154 156 }
+5 -2
drivers/cpufreq/loongson3_cpufreq.c
··· 346 346 { 347 347 int i, ret; 348 348 349 - for (i = 0; i < MAX_PACKAGES; i++) 350 - devm_mutex_init(&pdev->dev, &cpufreq_mutex[i]); 349 + for (i = 0; i < MAX_PACKAGES; i++) { 350 + ret = devm_mutex_init(&pdev->dev, &cpufreq_mutex[i]); 351 + if (ret) 352 + return ret; 353 + } 351 354 352 355 ret = do_service_request(0, 0, CMD_GET_VERSION, 0, 0); 353 356 if (ret <= 0)
+1 -1
drivers/cpufreq/mediatek-cpufreq-hw.c
··· 62 62 63 63 policy = cpufreq_cpu_get_raw(cpu_dev->id); 64 64 if (!policy) 65 - return 0; 65 + return -EINVAL; 66 66 67 67 data = policy->driver_data; 68 68
+3 -1
drivers/cpufreq/scmi-cpufreq.c
··· 287 287 ret = cpufreq_enable_boost_support(); 288 288 if (ret) { 289 289 dev_warn(cpu_dev, "failed to enable boost: %d\n", ret); 290 - goto out_free_opp; 290 + goto out_free_table; 291 291 } else { 292 292 scmi_cpufreq_hw_attr[1] = &cpufreq_freq_attr_scaling_boost_freqs; 293 293 scmi_cpufreq_driver.boost_enabled = true; ··· 296 296 297 297 return 0; 298 298 299 + out_free_table: 300 + dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); 299 301 out_free_opp: 300 302 dev_pm_opp_remove_all_dynamic(cpu_dev); 301 303
+28
drivers/cpufreq/sun50i-cpufreq-nvmem.c
··· 22 22 #define NVMEM_MASK 0x7 23 23 #define NVMEM_SHIFT 5 24 24 25 + #define SUN50I_A100_NVMEM_MASK 0xf 26 + #define SUN50I_A100_NVMEM_SHIFT 12 27 + 25 28 static struct platform_device *cpufreq_dt_pdev, *sun50i_cpufreq_pdev; 26 29 27 30 struct sunxi_cpufreq_data { ··· 46 43 return efuse_value - 1; 47 44 else 48 45 return 0; 46 + } 47 + 48 + static u32 sun50i_a100_efuse_xlate(u32 speedbin) 49 + { 50 + u32 efuse_value; 51 + 52 + efuse_value = (speedbin >> SUN50I_A100_NVMEM_SHIFT) & 53 + SUN50I_A100_NVMEM_MASK; 54 + 55 + switch (efuse_value) { 56 + case 0b100: 57 + return 2; 58 + case 0b010: 59 + return 1; 60 + default: 61 + return 0; 62 + } 49 63 } 50 64 51 65 static int get_soc_id_revision(void) ··· 128 108 .efuse_xlate = sun50i_h6_efuse_xlate, 129 109 }; 130 110 111 + static struct sunxi_cpufreq_data sun50i_a100_cpufreq_data = { 112 + .efuse_xlate = sun50i_a100_efuse_xlate, 113 + }; 114 + 131 115 static struct sunxi_cpufreq_data sun50i_h616_cpufreq_data = { 132 116 .efuse_xlate = sun50i_h616_efuse_xlate, 133 117 }; ··· 139 115 static const struct of_device_id cpu_opp_match_list[] = { 140 116 { .compatible = "allwinner,sun50i-h6-operating-points", 141 117 .data = &sun50i_h6_cpufreq_data, 118 + }, 119 + { .compatible = "allwinner,sun50i-a100-operating-points", 120 + .data = &sun50i_a100_cpufreq_data, 142 121 }, 143 122 { .compatible = "allwinner,sun50i-h616-operating-points", 144 123 .data = &sun50i_h616_cpufreq_data, ··· 318 291 319 292 static const struct of_device_id sun50i_cpufreq_match_list[] = { 320 293 { .compatible = "allwinner,sun50i-h6" }, 294 + { .compatible = "allwinner,sun50i-a100" }, 321 295 { .compatible = "allwinner,sun50i-h616" }, 322 296 { .compatible = "allwinner,sun50i-h618" }, 323 297 { .compatible = "allwinner,sun50i-h700" },
+7 -3
drivers/cpufreq/ti-cpufreq.c
··· 93 93 bool multi_regulator; 94 94 /* Backward compatibility hack: Might have missing syscon */ 95 95 #define TI_QUIRK_SYSCON_MAY_BE_MISSING 0x1 96 + /* Backward compatibility hack: new syscon size is 1 register wide */ 97 + #define TI_QUIRK_SYSCON_IS_SINGLE_REG 0x2 96 98 u8 quirks; 97 99 }; 98 100 ··· 318 316 .efuse_offset = 0x0018, 319 317 .efuse_mask = 0x07c0, 320 318 .efuse_shift = 0x6, 321 - .rev_offset = 0x0014, 322 319 .multi_regulator = false, 320 + .quirks = TI_QUIRK_SYSCON_IS_SINGLE_REG, 323 321 }; 324 322 325 323 static struct ti_cpufreq_soc_data am62a7_soc_data = { ··· 327 325 .efuse_offset = 0x0, 328 326 .efuse_mask = 0x07c0, 329 327 .efuse_shift = 0x6, 330 - .rev_offset = 0x0014, 331 328 .multi_regulator = false, 332 329 }; 333 330 ··· 335 334 .efuse_offset = 0x0, 336 335 .efuse_mask = 0x07c0, 337 336 .efuse_shift = 0x6, 338 - .rev_offset = 0x0014, 339 337 .multi_regulator = false, 340 338 }; 341 339 ··· 354 354 355 355 ret = regmap_read(opp_data->syscon, opp_data->soc_data->efuse_offset, 356 356 &efuse); 357 + 358 + if (opp_data->soc_data->quirks & TI_QUIRK_SYSCON_IS_SINGLE_REG && ret == -EIO) 359 + ret = regmap_read(opp_data->syscon, 0x0, &efuse); 360 + 357 361 if (opp_data->soc_data->quirks & TI_QUIRK_SYSCON_MAY_BE_MISSING && ret == -EIO) { 358 362 /* not a syscon register! */ 359 363 void __iomem *regs = ioremap(OMAP3_SYSCON_BASE +
+333
drivers/cpufreq/virtual-cpufreq.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (C) 2024 Google LLC 4 + */ 5 + 6 + #include <linux/arch_topology.h> 7 + #include <linux/cpufreq.h> 8 + #include <linux/init.h> 9 + #include <linux/sched.h> 10 + #include <linux/kernel.h> 11 + #include <linux/module.h> 12 + #include <linux/of_address.h> 13 + #include <linux/of_platform.h> 14 + #include <linux/platform_device.h> 15 + #include <linux/slab.h> 16 + 17 + /* 18 + * CPU0..CPUn 19 + * +-------------+-------------------------------+--------+-------+ 20 + * | Register | Description | Offset | Len | 21 + * +-------------+-------------------------------+--------+-------+ 22 + * | cur_perf | read this register to get | 0x0 | 0x4 | 23 + * | | the current perf (integer val | | | 24 + * | | representing perf relative to | | | 25 + * | | max performance) | | | 26 + * | | that vCPU is running at | | | 27 + * +-------------+-------------------------------+--------+-------+ 28 + * | set_perf | write to this register to set | 0x4 | 0x4 | 29 + * | | perf value of the vCPU | | | 30 + * +-------------+-------------------------------+--------+-------+ 31 + * | perftbl_len | number of entries in perf | 0x8 | 0x4 | 32 + * | | table. A single entry in the | | | 33 + * | | perf table denotes no table | | | 34 + * | | and the entry contains | | | 35 + * | | the maximum perf value | | | 36 + * | | that this vCPU supports. | | | 37 + * | | The guest can request any | | | 38 + * | | value between 1 and max perf | | | 39 + * | | when perftbls are not used. | | | 40 + * +---------------------------------------------+--------+-------+ 41 + * | perftbl_sel | write to this register to | 0xc | 0x4 | 42 + * | | select perf table entry to | | | 43 + * | | read from | | | 44 + * +---------------------------------------------+--------+-------+ 45 + * | perftbl_rd | read this register to get | 0x10 | 0x4 | 46 + * | | perf value of the selected | | | 47 + * | | entry based on perftbl_sel | | | 48 + * +---------------------------------------------+--------+-------+ 49 + * | perf_domain | performance domain number | 0x14 | 0x4 | 50 + * | | that this vCPU belongs to. | | | 51 + * | | vCPUs sharing the same perf | | | 52 + * | | domain number are part of the | | | 53 + * | | same performance domain. | | | 54 + * +-------------+-------------------------------+--------+-------+ 55 + */ 56 + 57 + #define REG_CUR_PERF_STATE_OFFSET 0x0 58 + #define REG_SET_PERF_STATE_OFFSET 0x4 59 + #define REG_PERFTBL_LEN_OFFSET 0x8 60 + #define REG_PERFTBL_SEL_OFFSET 0xc 61 + #define REG_PERFTBL_RD_OFFSET 0x10 62 + #define REG_PERF_DOMAIN_OFFSET 0x14 63 + #define PER_CPU_OFFSET 0x1000 64 + 65 + #define PERFTBL_MAX_ENTRIES 64U 66 + 67 + static void __iomem *base; 68 + static DEFINE_PER_CPU(u32, perftbl_num_entries); 69 + 70 + static void virt_scale_freq_tick(void) 71 + { 72 + int cpu = smp_processor_id(); 73 + u32 max_freq = (u32)cpufreq_get_hw_max_freq(cpu); 74 + u64 cur_freq; 75 + unsigned long scale; 76 + 77 + cur_freq = (u64)readl_relaxed(base + cpu * PER_CPU_OFFSET 78 + + REG_CUR_PERF_STATE_OFFSET); 79 + 80 + cur_freq <<= SCHED_CAPACITY_SHIFT; 81 + scale = (unsigned long)div_u64(cur_freq, max_freq); 82 + scale = min(scale, SCHED_CAPACITY_SCALE); 83 + 84 + this_cpu_write(arch_freq_scale, scale); 85 + } 86 + 87 + static struct scale_freq_data virt_sfd = { 88 + .source = SCALE_FREQ_SOURCE_VIRT, 89 + .set_freq_scale = virt_scale_freq_tick, 90 + }; 91 + 92 + static unsigned int virt_cpufreq_set_perf(struct cpufreq_policy *policy, 93 + unsigned int target_freq) 94 + { 95 + writel_relaxed(target_freq, 96 + base + policy->cpu * PER_CPU_OFFSET + REG_SET_PERF_STATE_OFFSET); 97 + return 0; 98 + } 99 + 100 + static unsigned int virt_cpufreq_fast_switch(struct cpufreq_policy *policy, 101 + unsigned int target_freq) 102 + { 103 + virt_cpufreq_set_perf(policy, target_freq); 104 + return target_freq; 105 + } 106 + 107 + static u32 virt_cpufreq_get_perftbl_entry(int cpu, u32 idx) 108 + { 109 + writel_relaxed(idx, base + cpu * PER_CPU_OFFSET + 110 + REG_PERFTBL_SEL_OFFSET); 111 + return readl_relaxed(base + cpu * PER_CPU_OFFSET + 112 + REG_PERFTBL_RD_OFFSET); 113 + } 114 + 115 + static int virt_cpufreq_target(struct cpufreq_policy *policy, 116 + unsigned int target_freq, 117 + unsigned int relation) 118 + { 119 + struct cpufreq_freqs freqs; 120 + int ret = 0; 121 + 122 + freqs.old = policy->cur; 123 + freqs.new = target_freq; 124 + 125 + cpufreq_freq_transition_begin(policy, &freqs); 126 + ret = virt_cpufreq_set_perf(policy, target_freq); 127 + cpufreq_freq_transition_end(policy, &freqs, ret != 0); 128 + 129 + return ret; 130 + } 131 + 132 + static int virt_cpufreq_get_sharing_cpus(struct cpufreq_policy *policy) 133 + { 134 + u32 cur_perf_domain, perf_domain; 135 + struct device *cpu_dev; 136 + int cpu; 137 + 138 + cur_perf_domain = readl_relaxed(base + policy->cpu * 139 + PER_CPU_OFFSET + REG_PERF_DOMAIN_OFFSET); 140 + 141 + for_each_possible_cpu(cpu) { 142 + cpu_dev = get_cpu_device(cpu); 143 + if (!cpu_dev) 144 + continue; 145 + 146 + perf_domain = readl_relaxed(base + cpu * 147 + PER_CPU_OFFSET + REG_PERF_DOMAIN_OFFSET); 148 + 149 + if (perf_domain == cur_perf_domain) 150 + cpumask_set_cpu(cpu, policy->cpus); 151 + } 152 + 153 + return 0; 154 + } 155 + 156 + static int virt_cpufreq_get_freq_info(struct cpufreq_policy *policy) 157 + { 158 + struct cpufreq_frequency_table *table; 159 + u32 num_perftbl_entries, idx; 160 + 161 + num_perftbl_entries = per_cpu(perftbl_num_entries, policy->cpu); 162 + 163 + if (num_perftbl_entries == 1) { 164 + policy->cpuinfo.min_freq = 1; 165 + policy->cpuinfo.max_freq = virt_cpufreq_get_perftbl_entry(policy->cpu, 0); 166 + 167 + policy->min = policy->cpuinfo.min_freq; 168 + policy->max = policy->cpuinfo.max_freq; 169 + 170 + policy->cur = policy->max; 171 + return 0; 172 + } 173 + 174 + table = kcalloc(num_perftbl_entries + 1, sizeof(*table), GFP_KERNEL); 175 + if (!table) 176 + return -ENOMEM; 177 + 178 + for (idx = 0; idx < num_perftbl_entries; idx++) 179 + table[idx].frequency = virt_cpufreq_get_perftbl_entry(policy->cpu, idx); 180 + 181 + table[idx].frequency = CPUFREQ_TABLE_END; 182 + policy->freq_table = table; 183 + 184 + return 0; 185 + } 186 + 187 + static int virt_cpufreq_cpu_init(struct cpufreq_policy *policy) 188 + { 189 + struct device *cpu_dev; 190 + int ret; 191 + 192 + cpu_dev = get_cpu_device(policy->cpu); 193 + if (!cpu_dev) 194 + return -ENODEV; 195 + 196 + ret = virt_cpufreq_get_freq_info(policy); 197 + if (ret) { 198 + dev_warn(cpu_dev, "failed to get cpufreq info\n"); 199 + return ret; 200 + } 201 + 202 + ret = virt_cpufreq_get_sharing_cpus(policy); 203 + if (ret) { 204 + dev_warn(cpu_dev, "failed to get sharing cpumask\n"); 205 + return ret; 206 + } 207 + 208 + /* 209 + * To simplify and improve latency of handling frequency requests on 210 + * the host side, this ensures that the vCPU thread triggering the MMIO 211 + * abort is the same thread whose performance constraints (Ex. uclamp 212 + * settings) need to be updated. This simplifies the VMM (Virtual 213 + * Machine Manager) having to find the correct vCPU thread and/or 214 + * facing permission issues when configuring other threads. 215 + */ 216 + policy->dvfs_possible_from_any_cpu = false; 217 + policy->fast_switch_possible = true; 218 + 219 + /* 220 + * Using the default SCALE_FREQ_SOURCE_CPUFREQ is insufficient since 221 + * the actual physical CPU frequency may not match requested frequency 222 + * from the vCPU thread due to frequency update latencies or other 223 + * inputs to the physical CPU frequency selection. This additional FIE 224 + * source allows for more accurate freq_scale updates and only takes 225 + * effect if another FIE source such as AMUs have not been registered. 226 + */ 227 + topology_set_scale_freq_source(&virt_sfd, policy->cpus); 228 + 229 + return 0; 230 + } 231 + 232 + static void virt_cpufreq_cpu_exit(struct cpufreq_policy *policy) 233 + { 234 + topology_clear_scale_freq_source(SCALE_FREQ_SOURCE_VIRT, policy->related_cpus); 235 + kfree(policy->freq_table); 236 + } 237 + 238 + static int virt_cpufreq_online(struct cpufreq_policy *policy) 239 + { 240 + /* Nothing to restore. */ 241 + return 0; 242 + } 243 + 244 + static int virt_cpufreq_offline(struct cpufreq_policy *policy) 245 + { 246 + /* Dummy offline() to avoid exit() being called and freeing resources. */ 247 + return 0; 248 + } 249 + 250 + static int virt_cpufreq_verify_policy(struct cpufreq_policy_data *policy) 251 + { 252 + if (policy->freq_table) 253 + return cpufreq_frequency_table_verify(policy, policy->freq_table); 254 + 255 + cpufreq_verify_within_cpu_limits(policy); 256 + return 0; 257 + } 258 + 259 + static struct cpufreq_driver cpufreq_virt_driver = { 260 + .name = "virt-cpufreq", 261 + .init = virt_cpufreq_cpu_init, 262 + .exit = virt_cpufreq_cpu_exit, 263 + .online = virt_cpufreq_online, 264 + .offline = virt_cpufreq_offline, 265 + .verify = virt_cpufreq_verify_policy, 266 + .target = virt_cpufreq_target, 267 + .fast_switch = virt_cpufreq_fast_switch, 268 + .attr = cpufreq_generic_attr, 269 + }; 270 + 271 + static int virt_cpufreq_driver_probe(struct platform_device *pdev) 272 + { 273 + u32 num_perftbl_entries; 274 + int ret, cpu; 275 + 276 + base = devm_platform_ioremap_resource(pdev, 0); 277 + if (IS_ERR(base)) 278 + return PTR_ERR(base); 279 + 280 + for_each_possible_cpu(cpu) { 281 + num_perftbl_entries = readl_relaxed(base + cpu * PER_CPU_OFFSET + 282 + REG_PERFTBL_LEN_OFFSET); 283 + 284 + if (!num_perftbl_entries || num_perftbl_entries > PERFTBL_MAX_ENTRIES) 285 + return -ENODEV; 286 + 287 + per_cpu(perftbl_num_entries, cpu) = num_perftbl_entries; 288 + } 289 + 290 + ret = cpufreq_register_driver(&cpufreq_virt_driver); 291 + if (ret) { 292 + dev_err(&pdev->dev, "Virtual CPUFreq driver failed to register: %d\n", ret); 293 + return ret; 294 + } 295 + 296 + dev_dbg(&pdev->dev, "Virtual CPUFreq driver initialized\n"); 297 + return 0; 298 + } 299 + 300 + static void virt_cpufreq_driver_remove(struct platform_device *pdev) 301 + { 302 + cpufreq_unregister_driver(&cpufreq_virt_driver); 303 + } 304 + 305 + static const struct of_device_id virt_cpufreq_match[] = { 306 + { .compatible = "qemu,virtual-cpufreq", .data = NULL}, 307 + {} 308 + }; 309 + MODULE_DEVICE_TABLE(of, virt_cpufreq_match); 310 + 311 + static struct platform_driver virt_cpufreq_driver = { 312 + .probe = virt_cpufreq_driver_probe, 313 + .remove = virt_cpufreq_driver_remove, 314 + .driver = { 315 + .name = "virt-cpufreq", 316 + .of_match_table = virt_cpufreq_match, 317 + }, 318 + }; 319 + 320 + static int __init virt_cpufreq_init(void) 321 + { 322 + return platform_driver_register(&virt_cpufreq_driver); 323 + } 324 + postcore_initcall(virt_cpufreq_init); 325 + 326 + static void __exit virt_cpufreq_exit(void) 327 + { 328 + platform_driver_unregister(&virt_cpufreq_driver); 329 + } 330 + module_exit(virt_cpufreq_exit); 331 + 332 + MODULE_DESCRIPTION("Virtual cpufreq driver"); 333 + MODULE_LICENSE("GPL");
+7 -3
drivers/cpuidle/cpuidle.c
··· 69 69 if (!drv) 70 70 return -ENODEV; 71 71 72 - /* Find lowest-power state that supports long-term idle */ 73 - for (i = drv->state_count - 1; i >= 0; i--) 72 + for (i = drv->state_count - 1; i >= 0; i--) { 74 73 if (drv->states[i].enter_dead) 75 - return drv->states[i].enter_dead(dev, i); 74 + drv->states[i].enter_dead(dev, i); 75 + } 76 76 77 + /* 78 + * If :enter_dead() is successful, it will never return, so reaching 79 + * here means that all of them failed above or were not present. 80 + */ 77 81 return -ENODEV; 78 82 } 79 83
+1
include/linux/arch_topology.h
··· 45 45 SCALE_FREQ_SOURCE_CPUFREQ = 0, 46 46 SCALE_FREQ_SOURCE_ARCH, 47 47 SCALE_FREQ_SOURCE_CPPC, 48 + SCALE_FREQ_SOURCE_VIRT, 48 49 }; 49 50 50 51 struct scale_freq_data {
+1 -1
include/linux/cpuidle.h
··· 61 61 struct cpuidle_driver *drv, 62 62 int index); 63 63 64 - int (*enter_dead) (struct cpuidle_device *dev, int index); 64 + void (*enter_dead) (struct cpuidle_device *dev, int index); 65 65 66 66 /* 67 67 * CPUs execute ->enter_s2idle with the local tick or entire timekeeping