617 lines
19 KiB
Diff
617 lines
19 KiB
Diff
From e7e28e79988eb671051d0d2af0eb010314c83d41 Mon Sep 17 00:00:00 2001
|
|
From: Ying Fang <fangying1@huawei.com>
|
|
Date: Tue, 8 Feb 2022 21:01:09 +0800
|
|
Subject: [PATCH 24/24] arm64: Add the cpufreq device to show cpufreq info to
|
|
guest
|
|
|
|
On ARM64 platform, cpu frequency is retrieved via ACPI CPPC.
|
|
A virtual cpufreq device based on ACPI CPPC is created to
|
|
present cpu frequency info to the guest.
|
|
|
|
The default frequency is set to host cpu nominal frequency,
|
|
which is obtained from the host CPPC sysfs. Other performance
|
|
data are set to the same value, since we don't support guest
|
|
performance scaling here.
|
|
|
|
Performance counters are also not emulated and they simply
|
|
return 1 if read, and guest should fallback to use desired
|
|
performance value as the current performance.
|
|
|
|
Guest kernel version above 4.18 is required to make it work.
|
|
|
|
This series is backported from:
|
|
https://patchwork.kernel.org/cover/11379943/
|
|
|
|
Signed-off-by: Ying Fang <fangying1@huawei.com>
|
|
Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
|
|
---
|
|
configs/devices/aarch64-softmmu/default.mak | 1 +
|
|
hw/acpi/aml-build.c | 22 ++
|
|
hw/acpi/cpufreq.c | 283 ++++++++++++++++++++
|
|
hw/acpi/meson.build | 1 +
|
|
hw/arm/virt-acpi-build.c | 77 +++++-
|
|
hw/arm/virt.c | 13 +
|
|
hw/char/Kconfig | 4 +
|
|
include/hw/acpi/acpi-defs.h | 38 +++
|
|
include/hw/acpi/aml-build.h | 3 +
|
|
include/hw/arm/virt.h | 1 +
|
|
tests/data/acpi/virt/DSDT | Bin 5196 -> 5669 bytes
|
|
tests/data/acpi/virt/DSDT.memhp | Bin 6557 -> 7030 bytes
|
|
tests/data/acpi/virt/DSDT.numamem | Bin 5196 -> 5669 bytes
|
|
tests/data/acpi/virt/DSDT.pxb | Bin 7679 -> 8152 bytes
|
|
14 files changed, 441 insertions(+), 2 deletions(-)
|
|
create mode 100644 hw/acpi/cpufreq.c
|
|
|
|
diff --git a/configs/devices/aarch64-softmmu/default.mak b/configs/devices/aarch64-softmmu/default.mak
|
|
index cf43ac8da1..c7a710a0f1 100644
|
|
--- a/configs/devices/aarch64-softmmu/default.mak
|
|
+++ b/configs/devices/aarch64-softmmu/default.mak
|
|
@@ -6,3 +6,4 @@ include ../arm-softmmu/default.mak
|
|
CONFIG_XLNX_ZYNQMP_ARM=y
|
|
CONFIG_XLNX_VERSAL=y
|
|
CONFIG_SBSA_REF=y
|
|
+CONFIG_CPUFREQ=y
|
|
diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c
|
|
index bebf49622b..c4edaafa4a 100644
|
|
--- a/hw/acpi/aml-build.c
|
|
+++ b/hw/acpi/aml-build.c
|
|
@@ -1554,6 +1554,28 @@ Aml *aml_sleep(uint64_t msec)
|
|
return var;
|
|
}
|
|
|
|
+/* ACPI 5.0b: 6.4.3.7 Generic Register Descriptor */
|
|
+Aml *aml_generic_register(AmlRegionSpace rs, uint8_t reg_width,
|
|
+ uint8_t reg_offset, AmlAccessType type, uint64_t addr)
|
|
+{
|
|
+ int i;
|
|
+ Aml *var = aml_alloc();
|
|
+ build_append_byte(var->buf, 0x82); /* Generic Register Descriptor */
|
|
+ build_append_byte(var->buf, 0x0C); /* Length, bits[7:0] value = 0x0C */
|
|
+ build_append_byte(var->buf, 0); /* Length, bits[15:8] value = 0 */
|
|
+ build_append_byte(var->buf, rs); /* Address Space ID */
|
|
+ build_append_byte(var->buf, reg_width); /* Register Bit Width */
|
|
+ build_append_byte(var->buf, reg_offset); /* Register Bit Offset */
|
|
+ build_append_byte(var->buf, type); /* Access Size */
|
|
+
|
|
+ /* Register address */
|
|
+ for (i = 0; i < 8; i++) {
|
|
+ build_append_byte(var->buf, extract64(addr, i * 8, 8));
|
|
+ }
|
|
+
|
|
+ return var;
|
|
+}
|
|
+
|
|
static uint8_t Hex2Byte(const char *src)
|
|
{
|
|
int hi, lo;
|
|
diff --git a/hw/acpi/cpufreq.c b/hw/acpi/cpufreq.c
|
|
new file mode 100644
|
|
index 0000000000..a84db490b3
|
|
--- /dev/null
|
|
+++ b/hw/acpi/cpufreq.c
|
|
@@ -0,0 +1,283 @@
|
|
+/*
|
|
+ * ACPI CPPC register device
|
|
+ *
|
|
+ * Support for showing CPU frequency in guest OS.
|
|
+ *
|
|
+ * Copyright (c) 2019 HUAWEI TECHNOLOGIES CO.,LTD.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+
|
|
+ * You should have received a copy of the GNU General Public License along
|
|
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
+ */
|
|
+
|
|
+#include "qemu/osdep.h"
|
|
+#include "hw/sysbus.h"
|
|
+#include "chardev/char.h"
|
|
+#include "qemu/log.h"
|
|
+#include "trace.h"
|
|
+#include "qemu/option.h"
|
|
+#include "sysemu/sysemu.h"
|
|
+#include "hw/acpi/acpi-defs.h"
|
|
+#include "qemu/cutils.h"
|
|
+#include "qemu/error-report.h"
|
|
+#include "hw/boards.h"
|
|
+
|
|
+#define TYPE_CPUFREQ "cpufreq"
|
|
+#define CPUFREQ(obj) OBJECT_CHECK(CpuhzState, (obj), TYPE_CPUFREQ)
|
|
+#define NOMINAL_FREQ_FILE "/sys/devices/system/cpu/cpu0/acpi_cppc/nominal_freq"
|
|
+#define CPU_MAX_FREQ_FILE "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"
|
|
+#define HZ_MAX_LENGTH 1024
|
|
+#define MAX_SUPPORT_SPACE 0x10000
|
|
+
|
|
+/*
|
|
+ * Since Hi1616 will not support CPPC, we simply use its nominal frequency as
|
|
+ * the default.
|
|
+ */
|
|
+#define DEFAULT_HZ 2400
|
|
+
|
|
+int cppc_regs_offset[CPPC_REG_COUNT] = {
|
|
+ [HIGHEST_PERF] = 0,
|
|
+ [NOMINAL_PERF] = 4,
|
|
+ [LOW_NON_LINEAR_PERF] = 8,
|
|
+ [LOWEST_PERF] = 12,
|
|
+ [GUARANTEED_PERF] = 16,
|
|
+ [DESIRED_PERF] = 20,
|
|
+ [MIN_PERF] = -1,
|
|
+ [MAX_PERF] = -1,
|
|
+ [PERF_REDUC_TOLERANCE] = -1,
|
|
+ [TIME_WINDOW] = -1,
|
|
+ [CTR_WRAP_TIME] = -1,
|
|
+ [REFERENCE_CTR] = 24,
|
|
+ [DELIVERED_CTR] = 32,
|
|
+ [PERF_LIMITED] = 40,
|
|
+ [ENABLE] = -1,
|
|
+ [AUTO_SEL_ENABLE] = -1,
|
|
+ [AUTO_ACT_WINDOW] = -1,
|
|
+ [ENERGY_PERF] = -1,
|
|
+ [REFERENCE_PERF] = -1,
|
|
+ [LOWEST_FREQ] = 44,
|
|
+ [NOMINAL_FREQ] = 48,
|
|
+};
|
|
+
|
|
+typedef struct CpuhzState {
|
|
+ SysBusDevice parent_obj;
|
|
+
|
|
+ MemoryRegion iomem;
|
|
+ uint32_t HighestPerformance;
|
|
+ uint32_t NominalPerformance;
|
|
+ uint32_t LowestNonlinearPerformance;
|
|
+ uint32_t LowestPerformance;
|
|
+ uint32_t GuaranteedPerformance;
|
|
+ uint32_t DesiredPerformance;
|
|
+ uint64_t ReferencePerformanceCounter;
|
|
+ uint64_t DeliveredPerformanceCounter;
|
|
+ uint32_t PerformanceLimited;
|
|
+ uint32_t LowestFreq;
|
|
+ uint32_t NominalFreq;
|
|
+ uint32_t reg_size;
|
|
+} CpuhzState;
|
|
+
|
|
+
|
|
+static uint64_t cpufreq_read(void *opaque, hwaddr offset, unsigned size)
|
|
+{
|
|
+ CpuhzState *s = (CpuhzState *)opaque;
|
|
+ uint64_t r;
|
|
+ uint64_t n;
|
|
+
|
|
+ MachineState *ms = MACHINE(qdev_get_machine());
|
|
+ unsigned int smp_cpus = ms->smp.cpus;
|
|
+
|
|
+ if (offset >= smp_cpus * CPPC_REG_PER_CPU_STRIDE) {
|
|
+ warn_report("cpufreq_read: offset 0x%lx out of range", offset);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ n = offset % CPPC_REG_PER_CPU_STRIDE;
|
|
+ switch (n) {
|
|
+ case 0:
|
|
+ r = s->HighestPerformance;
|
|
+ break;
|
|
+ case 4:
|
|
+ r = s->NominalPerformance;
|
|
+ break;
|
|
+ case 8:
|
|
+ r = s->LowestNonlinearPerformance;
|
|
+ break;
|
|
+ case 12:
|
|
+ r = s->LowestPerformance;
|
|
+ break;
|
|
+ case 16:
|
|
+ r = s->GuaranteedPerformance;
|
|
+ break;
|
|
+ case 20:
|
|
+ r = s->DesiredPerformance;
|
|
+ break;
|
|
+ /*
|
|
+ * We don't have real counters and it is hard to emulate, so always set the
|
|
+ * counter value to 1 to rely on Linux to use the DesiredPerformance value
|
|
+ * directly.
|
|
+ */
|
|
+ case 24:
|
|
+ r = s->ReferencePerformanceCounter;
|
|
+ break;
|
|
+ /*
|
|
+ * Guest may still access the register by 32bit; add the process to
|
|
+ * eliminate unnecessary warnings.
|
|
+ */
|
|
+ case 28:
|
|
+ r = s->ReferencePerformanceCounter >> 32;
|
|
+ break;
|
|
+ case 32:
|
|
+ r = s->DeliveredPerformanceCounter;
|
|
+ break;
|
|
+ case 36:
|
|
+ r = s->DeliveredPerformanceCounter >> 32;
|
|
+ break;
|
|
+
|
|
+ case 40:
|
|
+ r = s->PerformanceLimited;
|
|
+ break;
|
|
+ case 44:
|
|
+ r = s->LowestFreq;
|
|
+ break;
|
|
+ case 48:
|
|
+ r = s->NominalFreq;
|
|
+ break;
|
|
+ default:
|
|
+ error_printf("cpufreq_read: Bad offset 0x%lx\n", offset);
|
|
+ r = 0;
|
|
+ break;
|
|
+ }
|
|
+ return r;
|
|
+}
|
|
+
|
|
+static void cpufreq_write(void *opaque, hwaddr offset,
|
|
+ uint64_t value, unsigned size)
|
|
+{
|
|
+ uint64_t n;
|
|
+ MachineState *ms = MACHINE(qdev_get_machine());
|
|
+ unsigned int smp_cpus = ms->smp.cpus;
|
|
+
|
|
+ if (offset >= smp_cpus * CPPC_REG_PER_CPU_STRIDE) {
|
|
+ error_printf("cpufreq_write: offset 0x%lx out of range", offset);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ n = offset % CPPC_REG_PER_CPU_STRIDE;
|
|
+
|
|
+ switch (n) {
|
|
+ case 20:
|
|
+ break;
|
|
+ default:
|
|
+ error_printf("cpufreq_write: Bad offset 0x%lx\n", offset);
|
|
+ }
|
|
+}
|
|
+
|
|
+static uint32_t CPPC_Read(const char *hostpath)
|
|
+{
|
|
+ int fd;
|
|
+ char buffer[HZ_MAX_LENGTH] = { 0 };
|
|
+ uint64_t hz;
|
|
+ int len;
|
|
+ const char *endptr = NULL;
|
|
+ int ret;
|
|
+
|
|
+ fd = qemu_open_old(hostpath, O_RDONLY);
|
|
+ if (fd < 0) {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ len = read(fd, buffer, HZ_MAX_LENGTH);
|
|
+ qemu_close(fd);
|
|
+ if (len <= 0) {
|
|
+ return 0;
|
|
+ }
|
|
+ ret = qemu_strtoul(buffer, &endptr, 0, &hz);
|
|
+ if (ret < 0) {
|
|
+ return 0;
|
|
+ }
|
|
+ return (uint32_t)hz;
|
|
+}
|
|
+
|
|
+static const MemoryRegionOps cpufreq_ops = {
|
|
+ .read = cpufreq_read,
|
|
+ .write = cpufreq_write,
|
|
+ .endianness = DEVICE_NATIVE_ENDIAN,
|
|
+};
|
|
+
|
|
+static void hz_init(CpuhzState *s)
|
|
+{
|
|
+ uint32_t hz;
|
|
+
|
|
+ hz = CPPC_Read(NOMINAL_FREQ_FILE);
|
|
+ if (hz == 0) {
|
|
+ hz = CPPC_Read(CPU_MAX_FREQ_FILE);
|
|
+ if (hz == 0) {
|
|
+ hz = DEFAULT_HZ;
|
|
+ } else {
|
|
+ /* Value in CpuMaxFrequency is in KHz unit; convert to MHz */
|
|
+ hz = hz / 1000;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ s->HighestPerformance = hz;
|
|
+ s->NominalPerformance = hz;
|
|
+ s->LowestNonlinearPerformance = hz;
|
|
+ s->LowestPerformance = hz;
|
|
+ s->GuaranteedPerformance = hz;
|
|
+ s->DesiredPerformance = hz;
|
|
+ s->ReferencePerformanceCounter = 1;
|
|
+ s->DeliveredPerformanceCounter = 1;
|
|
+ s->PerformanceLimited = 0;
|
|
+ s->LowestFreq = hz;
|
|
+ s->NominalFreq = hz;
|
|
+}
|
|
+
|
|
+static void cpufreq_init(Object *obj)
|
|
+{
|
|
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
|
+ CpuhzState *s = CPUFREQ(obj);
|
|
+
|
|
+ MachineState *ms = MACHINE(qdev_get_machine());
|
|
+ unsigned int smp_cpus = ms->smp.cpus;
|
|
+
|
|
+ s->reg_size = smp_cpus * CPPC_REG_PER_CPU_STRIDE;
|
|
+ if (s->reg_size > MAX_SUPPORT_SPACE) {
|
|
+ error_report("Required space 0x%x excesses the max support 0x%x",
|
|
+ s->reg_size, MAX_SUPPORT_SPACE);
|
|
+ goto err_end;
|
|
+ }
|
|
+
|
|
+ memory_region_init_io(&s->iomem, OBJECT(s), &cpufreq_ops, s, "cpufreq",
|
|
+ s->reg_size);
|
|
+ sysbus_init_mmio(sbd, &s->iomem);
|
|
+ hz_init(s);
|
|
+ return;
|
|
+
|
|
+err_end:
|
|
+ /* Set desired perf register offset to -1 to indicate no support for CPPC */
|
|
+ cppc_regs_offset[DESIRED_PERF] = -1;
|
|
+}
|
|
+
|
|
+static const TypeInfo cpufreq_arm_info = {
|
|
+ .name = TYPE_CPUFREQ,
|
|
+ .parent = TYPE_SYS_BUS_DEVICE,
|
|
+ .instance_size = sizeof(CpuhzState),
|
|
+ .instance_init = cpufreq_init,
|
|
+};
|
|
+
|
|
+static void cpufreq_register_types(void)
|
|
+{
|
|
+ type_register_static(&cpufreq_arm_info);
|
|
+}
|
|
+
|
|
+type_init(cpufreq_register_types)
|
|
diff --git a/hw/acpi/meson.build b/hw/acpi/meson.build
|
|
index adf6347bc4..448ea6afb4 100644
|
|
--- a/hw/acpi/meson.build
|
|
+++ b/hw/acpi/meson.build
|
|
@@ -25,6 +25,7 @@ acpi_ss.add(when: 'CONFIG_ACPI_X86_ICH', if_true: files('ich9.c', 'tco.c'))
|
|
acpi_ss.add(when: 'CONFIG_IPMI', if_true: files('ipmi.c'), if_false: files('ipmi-stub.c'))
|
|
acpi_ss.add(when: 'CONFIG_PC', if_false: files('acpi-x86-stub.c'))
|
|
acpi_ss.add(when: 'CONFIG_TPM', if_true: files('tpm.c'))
|
|
+acpi_ss.add(when: 'CONFIG_CPUFREQ', if_true: files('cpufreq.c'))
|
|
softmmu_ss.add(when: 'CONFIG_ACPI', if_false: files('acpi-stub.c', 'aml-build-stub.c', 'ghes-stub.c'))
|
|
softmmu_ss.add_all(when: 'CONFIG_ACPI', if_true: acpi_ss)
|
|
softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('acpi-stub.c', 'aml-build-stub.c',
|
|
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
|
|
index 674f902652..1ca705654b 100644
|
|
--- a/hw/arm/virt-acpi-build.c
|
|
+++ b/hw/arm/virt-acpi-build.c
|
|
@@ -60,7 +60,68 @@
|
|
|
|
#define ACPI_BUILD_TABLE_SIZE 0x20000
|
|
|
|
-static void acpi_dsdt_add_cpus(Aml *scope, VirtMachineState *vms)
|
|
+static void acpi_dsdt_add_psd(Aml *dev, int cpus)
|
|
+{
|
|
+ Aml *pkg;
|
|
+ Aml *sub;
|
|
+
|
|
+ sub = aml_package(5);
|
|
+ aml_append(sub, aml_int(5));
|
|
+ aml_append(sub, aml_int(0));
|
|
+ /* Assume all vCPUs belong to the same domain */
|
|
+ aml_append(sub, aml_int(0));
|
|
+ /* SW_ANY: OSPM coordinate, initiate on any processor */
|
|
+ aml_append(sub, aml_int(0xFD));
|
|
+ aml_append(sub, aml_int(cpus));
|
|
+
|
|
+ pkg = aml_package(1);
|
|
+ aml_append(pkg, sub);
|
|
+
|
|
+ aml_append(dev, aml_name_decl("_PSD", pkg));
|
|
+}
|
|
+
|
|
+static void acpi_dsdt_add_cppc(Aml *dev, uint64_t cpu_base, int *regs_offset)
|
|
+{
|
|
+ Aml *cpc;
|
|
+ int i;
|
|
+
|
|
+ /* Use version 3 of CPPC table from ACPI 6.3 */
|
|
+ cpc = aml_package(23);
|
|
+ aml_append(cpc, aml_int(23));
|
|
+ aml_append(cpc, aml_int(3));
|
|
+
|
|
+ for (i = 0; i < CPPC_REG_COUNT; i++) {
|
|
+ Aml *res;
|
|
+ uint8_t reg_width;
|
|
+ uint8_t acc_type;
|
|
+ uint64_t addr;
|
|
+
|
|
+ if (regs_offset[i] == -1) {
|
|
+ reg_width = 0;
|
|
+ acc_type = AML_ANY_ACC;
|
|
+ addr = 0;
|
|
+ } else {
|
|
+ addr = cpu_base + regs_offset[i];
|
|
+ if (i == REFERENCE_CTR || i == DELIVERED_CTR) {
|
|
+ reg_width = 64;
|
|
+ acc_type = AML_QWORD_ACC;
|
|
+ } else {
|
|
+ reg_width = 32;
|
|
+ acc_type = AML_DWORD_ACC;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ res = aml_resource_template();
|
|
+ aml_append(res, aml_generic_register(AML_SYSTEM_MEMORY, reg_width, 0,
|
|
+ acc_type, addr));
|
|
+ aml_append(cpc, res);
|
|
+ }
|
|
+
|
|
+ aml_append(dev, aml_name_decl("_CPC", cpc));
|
|
+}
|
|
+
|
|
+static void acpi_dsdt_add_cpus(Aml *scope, VirtMachineState *vms,
|
|
+ const MemMapEntry *cppc_memmap)
|
|
{
|
|
MachineState *ms = MACHINE(vms);
|
|
uint16_t i;
|
|
@@ -69,6 +130,18 @@ static void acpi_dsdt_add_cpus(Aml *scope, VirtMachineState *vms)
|
|
Aml *dev = aml_device("C%.03X", i);
|
|
aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0007")));
|
|
aml_append(dev, aml_name_decl("_UID", aml_int(i)));
|
|
+
|
|
+ /*
|
|
+ * Append _CPC and _PSD to support CPU frequence show
|
|
+ * Check CPPC available by DESIRED_PERF register
|
|
+ */
|
|
+ if (cppc_regs_offset[DESIRED_PERF] != -1) {
|
|
+ acpi_dsdt_add_cppc(dev,
|
|
+ cppc_memmap->base + i * CPPC_REG_PER_CPU_STRIDE,
|
|
+ cppc_regs_offset);
|
|
+ acpi_dsdt_add_psd(dev, ms->smp.cpus);
|
|
+ }
|
|
+
|
|
aml_append(scope, dev);
|
|
}
|
|
}
|
|
@@ -858,7 +931,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
|
|
* the RTC ACPI device at all when using UEFI.
|
|
*/
|
|
scope = aml_scope("\\_SB");
|
|
- acpi_dsdt_add_cpus(scope, vms);
|
|
+ acpi_dsdt_add_cpus(scope, vms, &memmap[VIRT_CPUFREQ]);
|
|
acpi_dsdt_add_uart(scope, &memmap[VIRT_UART],
|
|
(irqmap[VIRT_UART] + ARM_SPI_BASE));
|
|
if (vmc->acpi_expose_flash) {
|
|
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
|
|
index 529c0d38b6..0538d258fa 100644
|
|
--- a/hw/arm/virt.c
|
|
+++ b/hw/arm/virt.c
|
|
@@ -154,6 +154,7 @@ static const MemMapEntry base_memmap[] = {
|
|
[VIRT_PVTIME] = { 0x090a0000, 0x00010000 },
|
|
[VIRT_SECURE_GPIO] = { 0x090b0000, 0x00001000 },
|
|
[VIRT_MMIO] = { 0x0a000000, 0x00000200 },
|
|
+ [VIRT_CPUFREQ] = { 0x0b000000, 0x00010000 },
|
|
/* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
|
|
[VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 },
|
|
[VIRT_SECURE_MEM] = { 0x0e000000, 0x01000000 },
|
|
@@ -931,6 +932,16 @@ static void create_uart(const VirtMachineState *vms, int uart,
|
|
g_free(nodename);
|
|
}
|
|
|
|
+static void create_cpufreq(const VirtMachineState *vms, MemoryRegion *mem)
|
|
+{
|
|
+ hwaddr base = vms->memmap[VIRT_CPUFREQ].base;
|
|
+ DeviceState *dev = qdev_new("cpufreq");
|
|
+ SysBusDevice *s = SYS_BUS_DEVICE(dev);
|
|
+
|
|
+ sysbus_realize_and_unref(s, &error_fatal);
|
|
+ memory_region_add_subregion(mem, base, sysbus_mmio_get_region(s, 0));
|
|
+}
|
|
+
|
|
static void create_rtc(const VirtMachineState *vms)
|
|
{
|
|
char *nodename;
|
|
@@ -2190,6 +2201,8 @@ static void machvirt_init(MachineState *machine)
|
|
|
|
create_uart(vms, VIRT_UART, sysmem, serial_hd(0));
|
|
|
|
+ create_cpufreq(vms, sysmem);
|
|
+
|
|
if (vms->secure) {
|
|
create_secure_ram(vms, secure_sysmem, secure_tag_sysmem);
|
|
create_uart(vms, VIRT_SECURE_UART, secure_sysmem, serial_hd(1));
|
|
diff --git a/hw/char/Kconfig b/hw/char/Kconfig
|
|
index 6b6cf2fc1d..335a60c2c1 100644
|
|
--- a/hw/char/Kconfig
|
|
+++ b/hw/char/Kconfig
|
|
@@ -71,3 +71,7 @@ config GOLDFISH_TTY
|
|
|
|
config SHAKTI_UART
|
|
bool
|
|
+
|
|
+config CPUFREQ
|
|
+ bool
|
|
+ default y
|
|
diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h
|
|
index c97e8633ad..ab86583228 100644
|
|
--- a/include/hw/acpi/acpi-defs.h
|
|
+++ b/include/hw/acpi/acpi-defs.h
|
|
@@ -92,4 +92,42 @@ typedef struct AcpiFadtData {
|
|
#define ACPI_FADT_ARM_PSCI_COMPLIANT (1 << 0)
|
|
#define ACPI_FADT_ARM_PSCI_USE_HVC (1 << 1)
|
|
|
|
+/*
|
|
+ * CPPC register definition from kernel header
|
|
+ * include/acpi/cppc_acpi.h
|
|
+ * The last element is newly added for easy use
|
|
+ */
|
|
+enum cppc_regs {
|
|
+ HIGHEST_PERF,
|
|
+ NOMINAL_PERF,
|
|
+ LOW_NON_LINEAR_PERF,
|
|
+ LOWEST_PERF,
|
|
+ GUARANTEED_PERF,
|
|
+ DESIRED_PERF,
|
|
+ MIN_PERF,
|
|
+ MAX_PERF,
|
|
+ PERF_REDUC_TOLERANCE,
|
|
+ TIME_WINDOW,
|
|
+ CTR_WRAP_TIME,
|
|
+ REFERENCE_CTR,
|
|
+ DELIVERED_CTR,
|
|
+ PERF_LIMITED,
|
|
+ ENABLE,
|
|
+ AUTO_SEL_ENABLE,
|
|
+ AUTO_ACT_WINDOW,
|
|
+ ENERGY_PERF,
|
|
+ REFERENCE_PERF,
|
|
+ LOWEST_FREQ,
|
|
+ NOMINAL_FREQ,
|
|
+ CPPC_REG_COUNT,
|
|
+};
|
|
+
|
|
+#define CPPC_REG_PER_CPU_STRIDE 0x40
|
|
+
|
|
+/*
|
|
+ * Offset for each CPPC register; -1 for unavailable
|
|
+ * The whole register space is unavailable if desired perf offset is -1.
|
|
+ */
|
|
+extern int cppc_regs_offset[CPPC_REG_COUNT];
|
|
+
|
|
#endif
|
|
diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h
|
|
index 8e8ad8029e..2e00d2e208 100644
|
|
--- a/include/hw/acpi/aml-build.h
|
|
+++ b/include/hw/acpi/aml-build.h
|
|
@@ -429,6 +429,9 @@ Aml *aml_dma(AmlDmaType typ, AmlDmaBusMaster bm, AmlTransferSize sz,
|
|
uint8_t channel);
|
|
Aml *aml_sleep(uint64_t msec);
|
|
Aml *aml_i2c_serial_bus_device(uint16_t address, const char *resource_source);
|
|
+Aml *aml_generic_register(AmlRegionSpace rs, uint8_t reg_width,
|
|
+ uint8_t reg_offset, AmlAccessType type,
|
|
+ uint64_t addr);
|
|
|
|
/* Block AML object primitives */
|
|
Aml *aml_scope(const char *name_format, ...) GCC_FMT_ATTR(1, 2);
|
|
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
|
|
index dc6b66ffc8..a4356cf736 100644
|
|
--- a/include/hw/arm/virt.h
|
|
+++ b/include/hw/arm/virt.h
|
|
@@ -70,6 +70,7 @@ enum {
|
|
VIRT_GIC_REDIST,
|
|
VIRT_SMMU,
|
|
VIRT_UART,
|
|
+ VIRT_CPUFREQ,
|
|
VIRT_MMIO,
|
|
VIRT_RTC,
|
|
VIRT_FW_CFG,
|
|
|
|
--
|
|
2.27.0
|
|
|