Hi all,
I would like to support acpi-cpufreq for ARM. For that, Firstly, I made new file which is a apci-freq-arm.c. But some people, such as Rafael, Sudeep and Hanjun Guo, worried about that. What ther are worried about is there are too many duplicate code between acpi-cpufreq.c and acpi-cpifre-arm.c So, I tried to separate this code into 3 parts which are common, x86-specific and arm-specific code. I tried to separate soc-specific code from acpi-cpufreq.c as much as possible. But as you know, there are too much x86-specific code in acpi-cpufreq.c When I tried to implement acpi-cpufreq-arm, there were a lot of compile error(due to x86-specific code). So I used #ifdef CONFIG_X86 to solve those errors.(and some hack codes) In this patch, I mostly focused on cpufreq. Later I will remove #ifdef CONFIG_X86. I would like your reviews and comments.
This patch is based on "http://git.linaro.org/leg/acpi/leg-kernel.git"
Last commit : commit a493444ce7f1792b44897160454149dc31ca208b Author: Graeme Gregory graeme.gregory@linaro.org Date: Tue Feb 11 09:21:17 2014 +0000 linaro-configs: add enterprise-distro.conf Signed-off-by: Graeme Gregory graeme.gregory@linaro.org
Thanks Best Regards
Signed-off-by: Jonghwan Choi jhbird.choi@samsung.com --- arch/arm64/Kconfig | 12 ++ arch/arm64/include/asm/cpu.h | 3 +- arch/arm64/include/asm/processor.h | 2 + arch/arm64/kernel/process.c | 6 + arch/arm64/kernel/setup.c | 5 +- drivers/acpi/Kconfig | 3 +- drivers/acpi/acpi_processor.c | 4 + drivers/acpi/processor_idle.c | 12 ++ drivers/cpufreq/Kconfig | 2 +- drivers/cpufreq/Kconfig.arm | 16 ++ drivers/cpufreq/Makefile | 3 +- drivers/cpufreq/acpi-cpufreq.c | 419 +++++------------------------------- drivers/cpufreq/acpi-cpufreq.h | 68 ++++++ drivers/cpufreq/acpi-cpufreq_arm.c | 88 ++++++++ drivers/cpufreq/acpi-cpufreq_x86.c | 334 ++++++++++++++++++++++++++++ include/linux/acpi.h | 1 + 16 files changed, 604 insertions(+), 373 deletions(-) create mode 100644 drivers/cpufreq/acpi-cpufreq.h create mode 100644 drivers/cpufreq/acpi-cpufreq_arm.c create mode 100644 drivers/cpufreq/acpi-cpufreq_x86.c
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 94cc542..e150e60 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -108,6 +108,13 @@ config IOMMU_HELPER config KERNEL_MODE_NEON def_bool y
+config ARCH_HAS_CPUFREQ + bool + help + Internal node to signify that the ARCH has CPUFREQ support + and that the relevant menu configurations are displayed for + it. + source "init/Kconfig"
source "kernel/Kconfig.freezer" @@ -116,6 +123,7 @@ menu "Platform selection"
config ARCH_VEXPRESS bool "ARMv8 software model (Versatile Express)" + select ARCH_HAS_CPUFREQ select ARCH_REQUIRE_GPIOLIB select COMMON_CLK_VERSATILE select POWER_RESET_VEXPRESS @@ -325,6 +333,10 @@ endmenu
menu "CPU Power Management"
+if ARCH_HAS_CPUFREQ +source "drivers/cpufreq/Kconfig" +endif + source "drivers/cpuidle/Kconfig"
endmenu diff --git a/arch/arm64/include/asm/cpu.h b/arch/arm64/include/asm/cpu.h index 8625eb1..69db3e7 100644 --- a/arch/arm64/include/asm/cpu.h +++ b/arch/arm64/include/asm/cpu.h @@ -20,6 +20,7 @@ struct cpuinfo_arm { #endif };
-DECLARE_PER_CPU(struct cpuinfo_arm, cpu_data); +DECLARE_PER_CPU(struct cpuinfo_arm, cpu_info); +#define cpu_data(cpu) per_cpu(cpu_info, cpu)
#endif diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h index 50ce951..7196873 100644 --- a/arch/arm64/include/asm/processor.h +++ b/arch/arm64/include/asm/processor.h @@ -47,6 +47,8 @@ #define ARCH_LOW_ADDRESS_LIMIT PHYS_MASK #endif /* __KERNEL__ */
+enum idle_boot_override {IDLE_NO_OVERRIDE=0, IDLE_HALT, IDLE_NOMWAIT, + IDLE_POLL}; struct debug_info { /* Have we suspended stepping by a debugger? */ int suspended_step; diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index 1c0a9be..1c985a9 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -86,6 +86,12 @@ void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd); EXPORT_SYMBOL_GPL(arm_pm_restart);
/* + * Idle related variables and functions */ unsigned long +boot_option_idle_override = IDLE_NO_OVERRIDE; +EXPORT_SYMBOL(boot_option_idle_override); + +/* * This is our default idle handler. */ void arch_cpu_idle(void) diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index fecf272..99b973b 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -377,14 +377,15 @@ static int __init arm64_device_init(void) } arch_initcall(arm64_device_init);
-static DEFINE_PER_CPU(struct cpu, cpu_data); +DEFINE_PER_CPU(struct cpu, cpu_info); +EXPORT_PER_CPU_SYMBOL(cpu_info);
static int __init topology_init(void) { int i;
for_each_possible_cpu(i) { - struct cpu *cpu = &per_cpu(cpu_data, i); + struct cpu *cpu = &per_cpu(cpu_info, i); cpu->hotpluggable = 1; register_cpu(cpu, i); } diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index cbc5dfc..961211f 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -152,7 +152,7 @@ config ACPI_PROCESSOR tristate "Processor" select THERMAL select CPU_IDLE - depends on X86 || IA64 + depends on X86 || IA64 || ARM64 default y help This driver installs ACPI as the idle handler for Linux and uses diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c index c29c2c3..6a89856 100644 --- a/drivers/acpi/acpi_processor.c +++ b/drivers/acpi/acpi_processor.c @@ -177,6 +177,7 @@ static int acpi_processor_hotadd_init(struct acpi_processor *pr) cpu_maps_update_begin(); cpu_ho\12.36.155.51\jhbirdchoi\Kernel\leg-kerneltplug_begin();
+#ifdef CONFIG_X86 ret = acpi_map_lsapic(pr->handle, pr->apic_id, &pr->id); if (ret) goto out; @@ -186,6 +187,7 @@ static int acpi_processor_hotadd_init(struct acpi_processor *pr) acpi_unmap_lsapic(pr->id); goto out; } +#endif
/* * CPU got hot-added, but cpu_data is not initialized yet. Set a flag @@ -457,8 +459,10 @@ static void acpi_processor_remove(struct acpi_device *device) cpu_hotplug_begin();
/* Remove the CPU. */ +#ifdef CONFIG_X86 arch_unregister_cpu(pr->id); acpi_unmap_lsapic(pr->id); +#endif
cpu_hotplug_done(); cpu_maps_update_done(); diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 3dca36d..0556a0a 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -70,8 +70,10 @@ static DEFINE_PER_CPU(struct acpi_processor_cx * [CPUIDLE_STATE_MAX],
static int disabled_by_idle_boot_param(void) { +#ifdef CONFIG_X86 return boot_option_idle_override == IDLE_POLL || boot_option_idle_override == IDLE_HALT; +#endif }
/* @@ -118,7 +120,9 @@ static struct dmi_system_id processor_power_dmi_table[] = { static void acpi_safe_halt(void) { if (!tif_need_resched()) { +#ifdef CONFIG_X86 safe_halt(); +#endif local_irq_disable(); } } @@ -748,7 +752,11 @@ static int acpi_idle_play_dead(struct cpuidle_device *dev, int index) while (1) {
if (cx->entry_method == ACPI_CSTATE_HALT) +#ifdef CONFIG_X86 safe_halt(); +#else + acpi_safe_halt(); +#endif else if (cx->entry_method == ACPI_CSTATE_SYSTEMIO) { inb(cx->address); /* See comment in acpi_idle_do_entry() */ @@ -843,7 +851,9 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, } }
+#ifdef CONFIG_X86 acpi_unlazy_tlb(smp_processor_id()); +#endif
/* Tell the scheduler that we are going deep-idle: */ sched_clock_idle_sleep_event(); @@ -1121,7 +1131,9 @@ int acpi_processor_power_init(struct acpi_processor *pr)
if (!first_run) { dmi_check_system(processor_power_dmi_table); +#ifdef CONFIG_X86 max_cstate = acpi_processor_cstate_check(max_cstate); +#endif if (max_cstate < ACPI_C_STATES_MAX) printk(KERN_NOTICE "ACPI: processor limited to max C-state %d\n", diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 3a7202d..6417406 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -228,7 +228,7 @@ source "drivers/cpufreq/Kconfig.x86" endmenu
menu "ARM CPU frequency scaling drivers" -depends on ARM +depends on ARM64 source "drivers/cpufreq/Kconfig.arm" endmenu
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 3129749..ef00a4c 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -16,6 +16,21 @@ config ARM_DT_BL_CPUFREQ This enables probing via DT for Generic CPUfreq driver for ARM big.LITTLE platform. This gets frequency tables from DT.
+config ARM_ACPI_CPUFREQ + tristate "ACPI Processor P-States driver" + depends on ACPI_PROCESSOR + help + This driver adds a CPUFreq driver which utilizes the ACPI + Processor Performance States. + This driver also supports ARM CPUs. + + To compile this driver as a module, choose M here: the + module will be called acpi-cpufreq. + + For details, take a look at file:Documentation/cpu-freq/. + + If in doubt, say N. + config ARM_EXYNOS_CPUFREQ bool
@@ -249,3 +264,4 @@ config ARM_VEXPRESS_SPC_CPUFREQ help This add the CPUfreq driver support for Versatile Express big.LITTLE platforms using SPC for power management. + diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 0fd80cb..83e44f6 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -21,7 +21,8 @@ obj-$(CONFIG_GENERIC_CPUFREQ_CPU0) += cpufreq-cpu0.o # powernow-k8 can load then. ACPI is preferred to all other hardware-specific drivers. # speedstep-* is preferred over p4-clockmod.
-obj-$(CONFIG_X86_ACPI_CPUFREQ) += acpi-cpufreq.o +obj-$(CONFIG_X86_ACPI_CPUFREQ) += acpi-cpufreq.o acpi-cpufreq_x86.o +obj-$(CONFIG_ARM_ACPI_CPUFREQ) += acpi-cpufreq.o acpi-cpufreq_arm.o obj-$(CONFIG_X86_POWERNOW_K8) += powernow-k8.o obj-$(CONFIG_X86_PCC_CPUFREQ) += pcc-cpufreq.o obj-$(CONFIG_X86_POWERNOW_K6) += powernow-k6.o diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c index 18448a7..26bf7112 100644 --- a/drivers/cpufreq/acpi-cpufreq.c +++ b/drivers/cpufreq/acpi-cpufreq.c @@ -42,37 +42,17 @@
#include <acpi/processor.h>
-#include <asm/msr.h> +#include <asm/cpu.h> #include <asm/processor.h> -#include <asm/cpufeature.h> + +#include "acpi-cpufreq.h"
MODULE_AUTHOR("Paul Diefenbaugh, Dominik Brodowski"); MODULE_DESCRIPTION("ACPI Processor P-States Driver"); MODULE_LICENSE("GPL");
-#define PFX "acpi-cpufreq: " - -enum { - UNDEFINED_CAPABLE = 0, - SYSTEM_INTEL_MSR_CAPABLE, - SYSTEM_AMD_MSR_CAPABLE, - SYSTEM_IO_CAPABLE, -}; - -#define INTEL_MSR_RANGE (0xffff) -#define AMD_MSR_RANGE (0x7) - -#define MSR_K7_HWCR_CPB_DIS (1ULL << 25) - -struct acpi_cpufreq_data { - struct acpi_processor_performance *acpi_data; - struct cpufreq_frequency_table *freq_table; - unsigned int resume; - unsigned int cpu_feature; - cpumask_var_t freqdomain_cpus; -}; - static DEFINE_PER_CPU(struct acpi_cpufreq_data *, acfreq_data); +static struct acpi_cpufreq_common *acpi;
/* acpi_perf_data is a pointer to percpu data. */ static struct acpi_processor_performance __percpu *acpi_perf_data; @@ -80,62 +60,12 @@ static struct acpi_processor_performance __percpu *acpi_perf_data; static struct cpufreq_driver acpi_cpufreq_driver;
static unsigned int acpi_pstate_strict; -static struct msr __percpu *msrs; - -static bool boost_state(unsigned int cpu) -{ - u32 lo, hi; - u64 msr; - - switch (boot_cpu_data.x86_vendor) { - case X86_VENDOR_INTEL: - rdmsr_on_cpu(cpu, MSR_IA32_MISC_ENABLE, &lo, &hi); - msr = lo | ((u64)hi << 32); - return !(msr & MSR_IA32_MISC_ENABLE_TURBO_DISABLE); - case X86_VENDOR_AMD: - rdmsr_on_cpu(cpu, MSR_K7_HWCR, &lo, &hi); - msr = lo | ((u64)hi << 32); - return !(msr & MSR_K7_HWCR_CPB_DIS); - } - return false; -} - -static void boost_set_msrs(bool enable, const struct cpumask *cpumask) -{ - u32 cpu; - u32 msr_addr; - u64 msr_mask; - - switch (boot_cpu_data.x86_vendor) { - case X86_VENDOR_INTEL: - msr_addr = MSR_IA32_MISC_ENABLE; - msr_mask = MSR_IA32_MISC_ENABLE_TURBO_DISABLE; - break; - case X86_VENDOR_AMD: - msr_addr = MSR_K7_HWCR; - msr_mask = MSR_K7_HWCR_CPB_DIS; - break; - default: - return; - } - - rdmsr_on_cpus(cpumask, msr_addr, msrs); - - for_each_cpu(cpu, cpumask) { - struct msr *reg = per_cpu_ptr(msrs, cpu); - if (enable) - reg->q &= ~msr_mask; - else - reg->q |= msr_mask; - } - - wrmsr_on_cpus(cpumask, msr_addr, msrs); -}
static int _store_boost(int val) { get_online_cpus(); - boost_set_msrs(val, cpu_online_mask); + if (acpi->ops->update_boost) + acpi->ops->update_boost(val, cpu_online_mask); put_online_cpus(); pr_debug("Core Boosting %sabled.\n", val ? "en" : "dis");
@@ -151,13 +81,12 @@ static ssize_t show_freqdomain_cpus(struct cpufreq_policy *policy, char *buf)
cpufreq_freq_attr_ro(freqdomain_cpus);
-#ifdef CONFIG_X86_ACPI_CPUFREQ_CPB static ssize_t store_boost(const char *buf, size_t count) { int ret; unsigned long val = 0;
- if (!acpi_cpufreq_driver.boost_supported) + if (!acpi->boost_supported) return -EINVAL;
ret = kstrtoul(buf, 10, &val); @@ -181,131 +110,23 @@ static ssize_t show_cpb(struct cpufreq_policy *policy, char *buf) }
cpufreq_freq_attr_rw(cpb); -#endif - -static int check_est_cpu(unsigned int cpuid) -{ - struct cpuinfo_x86 *cpu = &cpu_data(cpuid); - - return cpu_has(cpu, X86_FEATURE_EST); -} - -static int check_amd_hwpstate_cpu(unsigned int cpuid) -{ - struct cpuinfo_x86 *cpu = &cpu_data(cpuid); - - return cpu_has(cpu, X86_FEATURE_HW_PSTATE); -} - -static unsigned extract_io(u32 value, struct acpi_cpufreq_data *data) -{ - struct acpi_processor_performance *perf; - int i; - - perf = data->acpi_data; - - for (i = 0; i < perf->state_count; i++) { - if (value == perf->states[i].status) - return data->freq_table[i].frequency; - } - return 0; -} - -static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data) -{ - int i; - struct acpi_processor_performance *perf; - - if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) - msr &= AMD_MSR_RANGE; - else - msr &= INTEL_MSR_RANGE; - - perf = data->acpi_data; - - for (i = 0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { - if (msr == perf->states[data->freq_table[i].driver_data].status) - return data->freq_table[i].frequency; - } - return data->freq_table[0].frequency; -} - -static unsigned extract_freq(u32 val, struct acpi_cpufreq_data *data) -{ - switch (data->cpu_feature) { - case SYSTEM_INTEL_MSR_CAPABLE: - case SYSTEM_AMD_MSR_CAPABLE: - return extract_msr(val, data); - case SYSTEM_IO_CAPABLE: - return extract_io(val, data); - default: - return 0; - } -} - -struct msr_addr { - u32 reg; -}; - -struct io_addr { - u16 port; - u8 bit_width; -}; - -struct drv_cmd { - unsigned int type; - const struct cpumask *mask; - union { - struct msr_addr msr; - struct io_addr io; - } addr; - u32 val; -};
/* Called via smp_call_function_single(), on the target CPU */ static void do_drv_read(void *_cmd) { - struct drv_cmd *cmd = _cmd; - u32 h; + struct drv_cmd *cmd = _cmd; + struct acpi_cpufreq_data *data = per_cpu(acfreq_data, +cpumask_first(cmd->mask));
- switch (cmd->type) { - case SYSTEM_INTEL_MSR_CAPABLE: - case SYSTEM_AMD_MSR_CAPABLE: - rdmsr(cmd->addr.msr.reg, cmd->val, h); - break; - case SYSTEM_IO_CAPABLE: - acpi_os_read_port((acpi_io_address)cmd->addr.io.port, - &cmd->val, - (u32)cmd->addr.io.bit_width); - break; - default: - break; - } + acpi->ops->drv_read(cmd, data); }
/* Called via smp_call_function_many(), on the target CPUs */ static void do_drv_write(void *_cmd) { struct drv_cmd *cmd = _cmd; - u32 lo, hi; + struct acpi_cpufreq_data *data = per_cpu(acfreq_data, +cpumask_first(cmd->mask));
- switch (cmd->type) { - case SYSTEM_INTEL_MSR_CAPABLE: - rdmsr(cmd->addr.msr.reg, lo, hi); - lo = (lo & ~INTEL_MSR_RANGE) | (cmd->val & INTEL_MSR_RANGE); - wrmsr(cmd->addr.msr.reg, lo, hi); - break; - case SYSTEM_AMD_MSR_CAPABLE: - wrmsr(cmd->addr.msr.reg, cmd->val, 0); - break; - case SYSTEM_IO_CAPABLE: - acpi_os_write_port((acpi_io_address)cmd->addr.io.port, - cmd->val, - (u32)cmd->addr.io.bit_width); - break; - default: - break; - } + acpi->ops->drv_write(cmd, data); }
static void drv_read(struct drv_cmd *cmd) @@ -330,32 +151,13 @@ static void drv_write(struct drv_cmd *cmd)
static u32 get_cur_val(const struct cpumask *mask) { - struct acpi_processor_performance *perf; struct drv_cmd cmd;
if (unlikely(cpumask_empty(mask))) return 0;
- switch (per_cpu(acfreq_data, cpumask_first(mask))->cpu_feature) { - case SYSTEM_INTEL_MSR_CAPABLE: - cmd.type = SYSTEM_INTEL_MSR_CAPABLE; - cmd.addr.msr.reg = MSR_IA32_PERF_CTL; - break; - case SYSTEM_AMD_MSR_CAPABLE: - cmd.type = SYSTEM_AMD_MSR_CAPABLE; - cmd.addr.msr.reg = MSR_AMD_PERF_CTL; - break; - case SYSTEM_IO_CAPABLE: - cmd.type = SYSTEM_IO_CAPABLE; - perf = per_cpu(acfreq_data, cpumask_first(mask))->acpi_data; - cmd.addr.io.port = perf->control_register.address; - cmd.addr.io.bit_width = perf->control_register.bit_width; - break; - default: - return 0; - } - cmd.mask = mask; + drv_read(&cmd);
pr_debug("get_cur_val = %u\n", cmd.val); @@ -377,7 +179,7 @@ static unsigned int get_cur_freq_on_cpu(unsigned int cpu) }
cached_freq = data->freq_table[data->acpi_data->state].frequency; - freq = extract_freq(get_cur_val(cpumask_of(cpu)), data); + freq = acpi->ops->extract_freq(get_cur_val(cpumask_of(cpu)), data); if (freq != cached_freq) { /* * The dreaded BIOS frequency change behind our back. @@ -398,7 +200,7 @@ static unsigned int check_freqs(const struct cpumask *mask, unsigned int freq, unsigned int i;
for (i = 0; i < 100; i++) { - cur_freq = extract_freq(get_cur_val(mask), data); + cur_freq = acpi->ops->extract_freq(get_cur_val(mask), data); if (cur_freq == freq) return 1; udelay(10); @@ -434,34 +236,14 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, } }
- switch (data->cpu_feature) { - case SYSTEM_INTEL_MSR_CAPABLE: - cmd.type = SYSTEM_INTEL_MSR_CAPABLE; - cmd.addr.msr.reg = MSR_IA32_PERF_CTL; - cmd.val = (u32) perf->states[next_perf_state].control; - break; - case SYSTEM_AMD_MSR_CAPABLE: - cmd.type = SYSTEM_AMD_MSR_CAPABLE; - cmd.addr.msr.reg = MSR_AMD_PERF_CTL; - cmd.val = (u32) perf->states[next_perf_state].control; - break; - case SYSTEM_IO_CAPABLE: - cmd.type = SYSTEM_IO_CAPABLE; - cmd.addr.io.port = perf->control_register.address; - cmd.addr.io.bit_width = perf->control_register.bit_width; - cmd.val = (u32) perf->states[next_perf_state].control; - break; - default: - result = -ENODEV; - goto out; - } - /* cpufreq holds the hotplug lock, so we are safe from here on */ if (policy->shared_type != CPUFREQ_SHARED_TYPE_ANY) cmd.mask = policy->cpus; else cmd.mask = cpumask_of(policy->cpu);
+ cmd.val = (u32) perf->states[next_perf_state].control; + drv_write(&cmd);
if (acpi_pstate_strict) { @@ -484,7 +266,7 @@ static unsigned long acpi_cpufreq_guess_freq(struct acpi_cpufreq_data *data, unsigned int cpu) { struct acpi_processor_performance *perf = data->acpi_data; - +#ifdef CONFIG_X86 if (cpu_khz) { /* search the closest match to cpu_khz */ unsigned int i; @@ -501,11 +283,11 @@ acpi_cpufreq_guess_freq(struct acpi_cpufreq_data *data, unsigned int cpu) } perf->state = perf->state_count-1; return freqn; - } else { - /* assume CPU is at P0... */ - perf->state = 0; - return perf->states[0].core_frequency * 1000; } +#endif + /* assume CPU is at P0... */ + perf->state = 0; + return perf->states[0].core_frequency * 1000; }
static void free_acpi_perf_data(void) @@ -519,44 +301,6 @@ static void free_acpi_perf_data(void) free_percpu(acpi_perf_data); }
-static int boost_notify(struct notifier_block *nb, unsigned long action, - void *hcpu) -{ - unsigned cpu = (long)hcpu; - const struct cpumask *cpumask; - - cpumask = get_cpu_mask(cpu); - - /* - * Clear the boost-disable bit on the CPU_DOWN path so that - * this cpu cannot block the remaining ones from boosting. On - * the CPU_UP path we simply keep the boost-disable flag in - * sync with the current global state. - */ - - switch (action) { - case CPU_UP_PREPARE: - case CPU_UP_PREPARE_FROZEN: - boost_set_msrs(acpi_cpufreq_driver.boost_enabled, cpumask); - break; - - case CPU_DOWN_PREPARE: - case CPU_DOWN_PREPARE_FROZEN: - boost_set_msrs(1, cpumask); - break; - - default: - break; - } - - return NOTIFY_OK; -} - - -static struct notifier_block boost_nb = { - .notifier_call = boost_notify, -}; - /* * acpi_cpufreq_early_init - initialize ACPI P-States library * @@ -618,27 +362,6 @@ static const struct dmi_system_id sw_any_bug_dmi_table[] = { }, { } }; - -static int acpi_cpufreq_blacklist(struct cpuinfo_x86 *c) -{ - /* Intel Xeon Processor 7100 Series Specification Update - * http://www.intel.com/Assets/PDF/specupdate/314554.pdf - * AL30: A Machine Check Exception (MCE) Occurring during an - * Enhanced Intel SpeedStep Technology Ratio Change May Cause - * Both Processor Cores to Lock Up. */ - if (c->x86_vendor == X86_VENDOR_INTEL) { - if ((c->x86 == 15) && - (c->x86_model == 6) && - (c->x86_mask == 8)) { - printk(KERN_INFO "acpi-cpufreq: Intel(R) " - "Xeon(R) 7100 Errata AL30, processors may " - "lock up on frequency changes: disabling " - "acpi-cpufreq.\n"); - return -ENODEV; - } - } - return 0; -} #endif
static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) @@ -648,7 +371,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) unsigned int cpu = policy->cpu; struct acpi_cpufreq_data *data; unsigned int result = 0; - struct cpuinfo_x86 *c = &cpu_data(policy->cpu); + void *c = &cpu_data(policy->cpu); struct acpi_processor_performance *perf; #ifdef CONFIG_SMP static int blacklisted; @@ -659,9 +382,11 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) #ifdef CONFIG_SMP if (blacklisted) return blacklisted; - blacklisted = acpi_cpufreq_blacklist(c); - if (blacklisted) - return blacklisted; + if (acpi->ops->arch_check) { + blacklisted = acpi->ops->arch_check(c); + if (blacklisted) + return blacklisted; + } #endif
data = kzalloc(sizeof(*data), GFP_KERNEL); @@ -676,7 +401,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) data->acpi_data = per_cpu_ptr(acpi_perf_data, cpu); per_cpu(acfreq_data, cpu) = data;
- if (cpu_has(c, X86_FEATURE_CONSTANT_TSC)) + if (acpi->const_loops) acpi_cpufreq_driver.flags |= CPUFREQ_CONST_LOOPS;
result = acpi_processor_register_performance(data->acpi_data, cpu); @@ -700,13 +425,13 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) dmi_check_system(sw_any_bug_dmi_table); if (bios_with_sw_any_bug && !policy_is_shared(policy)) { policy->shared_type = CPUFREQ_SHARED_TYPE_ALL; - cpumask_copy(policy->cpus, cpu_core_mask(cpu)); +//FIXME cpumask_copy(policy->cpus, cpu_core_mask(cpu)); }
- if (check_amd_hwpstate_cpu(cpu) && !acpi_pstate_strict) { + if (acpi->feature_hw_pstate && !acpi_pstate_strict) { cpumask_clear(policy->cpus); cpumask_set_cpu(cpu, policy->cpus); - cpumask_copy(data->freqdomain_cpus, cpu_sibling_mask(cpu)); +//FIXME cpumask_copy(data->freqdomain_cpus, cpu_sibling_mask(cpu)); policy->shared_type = CPUFREQ_SHARED_TYPE_HW; pr_info_once(PFX "overriding BIOS provided _PSD data\n"); } @@ -724,32 +449,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) goto err_unreg; }
- switch (perf->control_register.space_id) { - case ACPI_ADR_SPACE_SYSTEM_IO: - if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD && - boot_cpu_data.x86 == 0xf) { - pr_debug("AMD K8 systems must use native drivers.\n"); - result = -ENODEV; - goto err_unreg; - } - pr_debug("SYSTEM IO addr space\n"); - data->cpu_feature = SYSTEM_IO_CAPABLE; - break; - case ACPI_ADR_SPACE_FIXED_HARDWARE: - pr_debug("HARDWARE addr space\n"); - if (check_est_cpu(cpu)) { - data->cpu_feature = SYSTEM_INTEL_MSR_CAPABLE; - break; - } - if (check_amd_hwpstate_cpu(cpu)) { - data->cpu_feature = SYSTEM_AMD_MSR_CAPABLE; - break; - } - result = -ENODEV; - goto err_unreg; - default: - pr_debug("Unknown addr space %d\n", - (u32) (perf->control_register.space_id)); + if (acpi->ops->arch_feature(data)) { result = -ENODEV; goto err_unreg; } @@ -899,34 +599,14 @@ static struct cpufreq_driver acpi_cpufreq_driver = {
static void __init acpi_cpufreq_boost_init(void) { - if (boot_cpu_has(X86_FEATURE_CPB) || boot_cpu_has(X86_FEATURE_IDA)) { - msrs = msrs_alloc(); - - if (!msrs) - return; - - acpi_cpufreq_driver.boost_supported = true; - acpi_cpufreq_driver.boost_enabled = boost_state(0); - get_online_cpus(); - - /* Force all MSRs to the same value */ - boost_set_msrs(acpi_cpufreq_driver.boost_enabled, - cpu_online_mask); - - register_cpu_notifier(&boost_nb); - - put_online_cpus(); - } + if (acpi->ops->arch_boost_init) + acpi->ops->arch_boost_init(acpi); }
static void acpi_cpufreq_boost_exit(void) { - if (msrs) { - unregister_cpu_notifier(&boost_nb); - - msrs_free(msrs); - msrs = NULL; - } + if (acpi->ops->arch_boost_exit) + acpi->ops->arch_boost_exit(); }
static int __init acpi_cpufreq_init(void) @@ -946,14 +626,23 @@ static int __init acpi_cpufreq_init(void) if (ret) return ret;
-#ifdef CONFIG_X86_ACPI_CPUFREQ_CPB + acpi = kzalloc(sizeof(*acpi), GFP_KERNEL); + if (!acpi) + return -ENOMEM; + + if (arch_acpi_cpufreq_init(acpi)) { //FIXME + kfree(acpi); + return -ENOMEM; + } + + /* this is a sysfs file with a strange name and an even stranger * semantic - per CPU instantiation, but system global effect. * Lets enable it only on AMD CPUs for compatibility reasons and * only if configured. This is considered legacy code, which * will probably be removed at some point in the future. */ - if (check_amd_hwpstate_cpu(0)) { + if (acpi->feature_hw_pstate) { struct freq_attr **iter;
pr_debug("adding sysfs entry for cpb\n"); @@ -965,11 +654,12 @@ static int __init acpi_cpufreq_init(void) if (iter[1] == NULL) *iter = &cpb; } -#endif + acpi_cpufreq_boost_init();
ret = cpufreq_register_driver(&acpi_cpufreq_driver); if (ret) { + kfree(acpi); free_acpi_perf_data(); acpi_cpufreq_boost_exit(); } @@ -985,6 +675,8 @@ static void __exit acpi_cpufreq_exit(void) cpufreq_unregister_driver(&acpi_cpufreq_driver);
free_acpi_perf_data(); + + kfree(acpi); }
module_param(acpi_pstate_strict, uint, 0644); @@ -995,13 +687,6 @@ MODULE_PARM_DESC(acpi_pstate_strict, late_initcall(acpi_cpufreq_init); module_exit(acpi_cpufreq_exit);
-static const struct x86_cpu_id acpi_cpufreq_ids[] = { - X86_FEATURE_MATCH(X86_FEATURE_ACPI), - X86_FEATURE_MATCH(X86_FEATURE_HW_PSTATE), - {} -}; -MODULE_DEVICE_TABLE(x86cpu, acpi_cpufreq_ids); - static const struct acpi_device_id processor_device_ids[] = { {ACPI_PROCESSOR_OBJECT_HID, }, {ACPI_PROCESSOR_DEVICE_HID, }, diff --git a/drivers/cpufreq/acpi-cpufreq.h b/drivers/cpufreq/acpi-cpufreq.h new file mode 100644 index 0000000..2e10c0e --- /dev/null +++ b/drivers/cpufreq/acpi-cpufreq.h @@ -0,0 +1,68 @@ +#ifndef _ACPI_CPUFREQ_H +#define _ACPI_CPUFREQ_H + +#define PFX "acpi-cpufreq: " + +enum { + UNDEFINED_CAPABLE = 0, + SYSTEM_INTEL_MSR_CAPABLE, + SYSTEM_AMD_MSR_CAPABLE, + SYSTEM_IO_CAPABLE, + SYSTEM_MEMORY_CAPABLE, +}; + +struct msr_addr { + u32 reg; +}; + +struct io_addr { + u16 port; + u32 bit_width; +}; + +struct mem_addr { + u64 addr; + u32 bit_width; +}; + +struct drv_cmd { + const struct cpumask *mask; + union { + struct msr_addr msr; + struct io_addr io; + struct mem_addr mem; + } addr; + u32 val; +}; + +struct acpi_cpufreq_data; +struct acpi_cpufreq_common; + +struct acpi_cpufreq_ops { + int (*arch_check)(void *info); + int (*arch_feature)(struct acpi_cpufreq_data *data); + void (*arch_boost_init)(struct acpi_cpufreq_common *acpi); + void (*arch_boost_exit)(void); + void (*drv_read)(void *cmd, struct acpi_cpufreq_data *data); + void (*drv_write)(void *cmd, struct acpi_cpufreq_data *data); + void (*update_boost)(bool enable, const struct cpumask *cpumask); + unsigned (*extract_freq)(u32 val, struct acpi_cpufreq_data *data); }; + +struct acpi_cpufreq_data { + struct acpi_processor_performance *acpi_data; + struct cpufreq_frequency_table *freq_table; + unsigned int resume; + unsigned int cpu_feature; + cpumask_var_t freqdomain_cpus; +}; + +struct acpi_cpufreq_common { + struct acpi_cpufreq_ops *ops; + bool boost_supported; + bool feature_hw_pstate; + bool const_loops; +}; + +int arch_acpi_cpufreq_init(struct acpi_cpufreq_common *common); +#endif /* _ACPI_CPUFREQ_H */ diff --git a/drivers/cpufreq/acpi-cpufreq_arm.c b/drivers/cpufreq/acpi-cpufreq_arm.c new file mode 100644 index 0000000..241c2a1 --- /dev/null +++ b/drivers/cpufreq/acpi-cpufreq_arm.c @@ -0,0 +1,88 @@ +#include <linux/acpi.h> +#include <linux/cpu.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/uaccess.h> + +#include <acpi/processor.h> + +#include <asm/cpu.h> +#include <asm/processor.h> + +#include "acpi-cpufreq.h" + +/* Called via smp_call_function_single(), on the target CPU */ static +void arm_do_drv_read(void *_cmd, struct acpi_cpufreq_data *data) { + struct drv_cmd *cmd = _cmd; + + if (data->cpu_feature != SYSTEM_MEMORY_CAPABLE) + return; + + acpi_os_read_memory((acpi_physical_address)cmd->addr.mem.addr, + (u64*)&cmd->val, + cmd->addr.mem.bit_width); +} + +/* Called via smp_call_function_many(), on the target CPUs */ static +void arm_do_drv_write(void *_cmd, struct acpi_cpufreq_data *data) { + struct drv_cmd *cmd = _cmd; + + if (data->cpu_feature != SYSTEM_MEMORY_CAPABLE) + return; + + acpi_os_write_memory((acpi_physical_address)cmd->addr.mem.addr, + (u64)cmd->val, + cmd->addr.mem.bit_width); +} + +static unsigned arm_extract_io(u32 value, struct acpi_cpufreq_data +*data) { + return 0; +} + +static unsigned arm_extract_freq(u32 val, struct acpi_cpufreq_data +*data) { + if (data->cpu_feature != SYSTEM_MEMORY_CAPABLE) + return 0; + + return arm_extract_io(val, data); +} + +static int arm_arch_feature(struct acpi_cpufreq_data *data) { + struct acpi_processor_performance *perf; + int result = 0; + + perf = data->acpi_data; + + switch (perf->control_register.space_id) { + case ACPI_ADR_SPACE_SYSTEM_MEMORY: + pr_debug("SYSTEM Memory addr space\n"); + data->cpu_feature = SYSTEM_MEMORY_CAPABLE; + break; + default: + pr_debug("Unknown addr space %d\n", + (u32) (perf->control_register.space_id)); + result = -ENODEV; + break; + } + + return result; +} + +static struct acpi_cpufreq_ops arm_ops = { + .arch_feature = arm_arch_feature, + .extract_freq = arm_extract_freq, + .drv_read = arm_do_drv_read, + .drv_write = arm_do_drv_write, +}; + +int arch_acpi_cpufreq_init(struct acpi_cpufreq_common *common) { + common->ops = &arm_ops; + + return 0; +} + diff --git a/drivers/cpufreq/acpi-cpufreq_x86.c b/drivers/cpufreq/acpi-cpufreq_x86.c new file mode 100644 index 0000000..82c9337 --- /dev/null +++ b/drivers/cpufreq/acpi-cpufreq_x86.c @@ -0,0 +1,334 @@ +#include <linux/module.h> +#include <linux/acpi.h> +#include <linux/cpu.h> +#include <linux/cpufreq.h> +#include <linux/io.h> + +#include <acpi/processor.h> + +#include <asm/msr.h> +#include <asm/cpufeature.h> +#include <asm/processor.h> + +#include "acpi-cpufreq.h" + +#define INTEL_MSR_RANGE (0xffff) +#define AMD_MSR_RANGE (0x7) + +#define MSR_K7_HWCR_CPB_DIS (1ULL << 25) + +static struct msr __percpu *msrs; + +static void x86_boost_set_msrs(bool enable, const struct cpumask +*cpumask) { + u32 cpu; + u32 msr_addr; + u64 msr_mask; + + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_INTEL: + msr_addr = MSR_IA32_MISC_ENABLE; + msr_mask = MSR_IA32_MISC_ENABLE_TURBO_DISABLE; + break; + case X86_VENDOR_AMD: + msr_addr = MSR_K7_HWCR; + msr_mask = MSR_K7_HWCR_CPB_DIS; + break; + default: + return; + } + + rdmsr_on_cpus(cpumask, msr_addr, msrs); + + for_each_cpu(cpu, cpumask) { + struct msr *reg = per_cpu_ptr(msrs, cpu); + if (enable) + reg->q &= ~msr_mask; + else + reg->q |= msr_mask; + } + + wrmsr_on_cpus(cpumask, msr_addr, msrs); } + +static bool boost_state(unsigned int cpu) { + u32 lo, hi; + u64 msr; + + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_INTEL: + rdmsr_on_cpu(cpu, MSR_IA32_MISC_ENABLE, &lo, &hi); + msr = lo | ((u64)hi << 32); + return !(msr & MSR_IA32_MISC_ENABLE_TURBO_DISABLE); + case X86_VENDOR_AMD: + rdmsr_on_cpu(cpu, MSR_K7_HWCR, &lo, &hi); + msr = lo | ((u64)hi << 32); + return !(msr & MSR_K7_HWCR_CPB_DIS); + } + return false; +} + +#ifdef CONFIG_SMP +static int x86_arch_check(void *data) +{ + struct cpuinfo_x86 *c = data; + /* Intel Xeon Processor 7100 Series Specification Update + * http://www.intel.com/Assets/PDF/specupdate/314554.pdf + * AL30: A Machine Check Exception (MCE) Occurring during an + * Enhanced Intel SpeedStep Technology Ratio Change May Cause + * Both Processor Cores to Lock Up. */ + if (c->x86_vendor == X86_VENDOR_INTEL) { + if ((c->x86 == 15) && + (c->x86_model == 6) && + (c->x86_mask == 8)) { + printk(KERN_INFO "acpi-cpufreq: Intel(R) " + "Xeon(R) 7100 Errata AL30, processors may " + "lock up on frequency changes: disabling " + "acpi-cpufreq.\n"); + return -ENODEV; + } + } + return 0; +} +#else +#define x86_arch_check NULL +#endif + +/* Called via smp_call_function_single(), on the target CPU */ static +void x86_do_drv_read(void *_cmd, struct acpi_cpufreq_data *data) { + struct acpi_processor_performance *perf; + struct drv_cmd *cmd = _cmd; + u32 h; + + switch (data->cpu_feature) { + case SYSTEM_INTEL_MSR_CAPABLE: + cmd->addr.msr.reg = MSR_IA32_PERF_CTL; + rdmsr(cmd->addr.msr.reg, cmd->val, h); + break; + case SYSTEM_AMD_MSR_CAPABLE: + cmd->addr.msr.reg = MSR_AMD_PERF_CTL; + rdmsr(cmd->addr.msr.reg, cmd->val, h); + break; + case SYSTEM_IO_CAPABLE: + perf = data->acpi_data; + cmd->addr.io.port = perf->control_register.address; + cmd->addr.io.bit_width = perf->control_register.bit_width; + acpi_os_read_port((acpi_io_address)cmd->addr.io.port, + &cmd->val, + (u32)cmd->addr.io.bit_width); + break; + } +} + +/* Called via smp_call_function_many(), on the target CPUs */ static +void x86_do_drv_write(void *_cmd, struct acpi_cpufreq_data *data) { + struct acpi_processor_performance *perf; + struct drv_cmd *cmd = _cmd; + u32 lo, hi; + + perf = data->acpi_data; + + switch (data->cpu_feature) { + case SYSTEM_INTEL_MSR_CAPABLE: + cmd->addr.msr.reg = MSR_IA32_PERF_CTL; + rdmsr(cmd->addr.msr.reg, lo, hi); + lo = (lo & ~INTEL_MSR_RANGE) | (cmd->val & INTEL_MSR_RANGE); + wrmsr(cmd->addr.msr.reg, lo, hi); + break; + case SYSTEM_AMD_MSR_CAPABLE: + cmd->addr.msr.reg = MSR_AMD_PERF_CTL; + wrmsr(cmd->addr.msr.reg, cmd->val, 0); + break; + case SYSTEM_IO_CAPABLE: + cmd->addr.io.port = perf->control_register.address; + cmd->addr.io.bit_width = perf->control_register.bit_width; + acpi_os_write_port((acpi_io_address)cmd->addr.io.port, + cmd->val, + (u32)cmd->addr.io.bit_width); + break; + } +} + +static unsigned extract_io(u32 value, struct acpi_cpufreq_data *data) { + struct acpi_processor_performance *perf; + int i; + + perf = data->acpi_data; + + for (i = 0; i < perf->state_count; i++) { + if (value == perf->states[i].status) + return data->freq_table[i].frequency; + } + return 0; +} + +static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data) { + int i; + struct acpi_processor_performance *perf; + + if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) + msr &= AMD_MSR_RANGE; + else + msr &= INTEL_MSR_RANGE; + + perf = data->acpi_data; + + for (i = 0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { + if (msr == perf->states[data->freq_table[i].driver_data].status) + return data->freq_table[i].frequency; + } + return data->freq_table[0].frequency; } + +unsigned x86_extract_freq(u32 val, struct acpi_cpufreq_data *data) { + switch (data->cpu_feature) { + case SYSTEM_INTEL_MSR_CAPABLE: + case SYSTEM_AMD_MSR_CAPABLE: + return extract_msr(val, data); + case SYSTEM_IO_CAPABLE: + return extract_io(val, data); + default: + return 0; + } +} + +static int boost_notify(struct notifier_block *nb, unsigned long action, + void *hcpu) +{ + unsigned cpu = (long)hcpu; + const struct cpumask *cpumask; + + cpumask = get_cpu_mask(cpu); + + /* + * Clear the boost-disable bit on the CPU_DOWN path so that + * this cpu cannot block the remaining ones from boosting. On + * the CPU_UP path we simply keep the boost-disable flag in + * sync with the current global state. + */ + + switch (action) { + case CPU_UP_PREPARE: + case CPU_UP_PREPARE_FROZEN: + x86_boost_set_msrs(boost_state(0), cpumask); + break; + + case CPU_DOWN_PREPARE: + case CPU_DOWN_PREPARE_FROZEN: + x86_boost_set_msrs(1, cpumask); + break; + + default: + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block boost_nb = { + .notifier_call = boost_notify, +}; + +static void x86_arch_boost_init(struct acpi_cpufreq_common *acpi) { + if (boot_cpu_has(X86_FEATURE_CPB) || boot_cpu_has(X86_FEATURE_IDA)) { + msrs = msrs_alloc(); + + if (!msrs) + return; + + acpi->boost_supported = true; + + get_online_cpus(); + + x86_boost_set_msrs(boost_state(0), cpu_online_mask); + + register_cpu_notifier(&boost_nb); + + put_online_cpus(); + } +} + +static void x86_arch_boost_exit(void) +{ + if (msrs) { + unregister_cpu_notifier(&boost_nb); + + msrs_free(msrs); + msrs = NULL; + } +} + +static int x86_arch_feature(struct acpi_cpufreq_data *data) { + struct acpi_processor_performance *perf; + int result = 0; + + perf = data->acpi_data; + + switch (perf->control_register.space_id) { + case ACPI_ADR_SPACE_SYSTEM_IO: + if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD && + boot_cpu_data.x86 == 0xf) { + pr_debug("AMD K8 systems must use native drivers.\n"); + result = -ENODEV; + break; + } + pr_debug("SYSTEM IO addr space\n"); + data->cpu_feature = SYSTEM_IO_CAPABLE; + break; + case ACPI_ADR_SPACE_FIXED_HARDWARE: + pr_debug("HARDWARE addr space\n"); + if (boot_cpu_has(X86_FEATURE_EST)) { + data->cpu_feature = SYSTEM_INTEL_MSR_CAPABLE; + break; + } + if (boot_cpu_has(X86_FEATURE_HW_PSTATE)) { + data->cpu_feature = SYSTEM_AMD_MSR_CAPABLE; + break; + } + result = -ENODEV; + break; + default: + pr_debug("Unknown addr space %d\n", + (u32) (perf->control_register.space_id)); + result = -ENODEV; + break; + } + + return result; +} + +static struct acpi_cpufreq_ops x86_ops = { + .arch_check = x86_arch_check, + .arch_feature = x86_arch_feature, + .arch_boost_init = x86_arch_boost_init, + .arch_boost_exit = x86_arch_boost_exit, + .drv_read = x86_do_drv_read, + .drv_write = x86_do_drv_write, + .update_boost = x86_boost_set_msrs, + .extract_freq = x86_extract_freq, +}; + +int __init arch_acpi_cpufreq_init(struct acpi_cpufreq_common *acpi) { + acpi->ops = &x86_ops; + + acpi->feature_hw_pstate = boot_cpu_has(X86_FEATURE_HW_PSTATE); + acpi->const_loops = boot_cpu_has(X86_FEATURE_CONSTANT_TSC); + + return 0; +} + +static const struct x86_cpu_id acpi_cpufreq_ids[] = { + X86_FEATURE_MATCH(X86_FEATURE_ACPI), + X86_FEATURE_MATCH(X86_FEATURE_HW_PSTATE), + {} +}; +MODULE_DEVICE_TABLE(x86cpu, acpi_cpufreq_ids); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 85c60f3..9630ccc 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -115,6 +115,7 @@ static inline void acpi_initrd_override(void *data, size_t size)
char * __acpi_map_table (unsigned long phys_addr, unsigned long size); void __acpi_unmap_table(char *map, unsigned long size); +int early_acpi_boot_init(void); int acpi_boot_init (void); void acpi_boot_table_init (void); int acpi_mps_check (void); -- 1.7.10.4
Hi Jonghwan,
On 14/04/14 06:53, Jonghwan Choi wrote:
Hi all,
I would like to support acpi-cpufreq for ARM. For that, Firstly, I made new file which is a apci-freq-arm.c. But some people, such as Rafael, Sudeep and Hanjun Guo, worried about that. What ther are worried about is there are too many duplicate code between acpi-cpufreq.c and acpi-cpifre-arm.c So, I tried to separate this code into 3 parts which are common, x86-specific and arm-specific code. I tried to separate soc-specific code from acpi-cpufreq.c as much as possible. But as you know, there are too much x86-specific code in acpi-cpufreq.c When I tried to implement acpi-cpufreq-arm, there were a lot of compile error(due to x86-specific code). So I used #ifdef CONFIG_X86 to solve those errors.(and some hack codes) In this patch, I mostly focused on cpufreq. Later I will remove #ifdef CONFIG_X86. I would like your reviews and comments.
This patch is based on "http://git.linaro.org/leg/acpi/leg-kernel.git"
Last commit : commit a493444ce7f1792b44897160454149dc31ca208b Author: Graeme Gregory graeme.gregory@linaro.org Date: Tue Feb 11 09:21:17 2014 +0000 linaro-configs: add enterprise-distro.conf Signed-off-by: Graeme Gregory graeme.gregory@linaro.org
Thanks Best Regards
Signed-off-by: Jonghwan Choi jhbird.choi@samsung.com
arch/arm64/Kconfig | 12 ++ arch/arm64/include/asm/cpu.h | 3 +- arch/arm64/include/asm/processor.h | 2 + arch/arm64/kernel/process.c | 6 + arch/arm64/kernel/setup.c | 5 +- drivers/acpi/Kconfig | 3 +- drivers/acpi/acpi_processor.c | 4 + drivers/acpi/processor_idle.c | 12 ++ drivers/cpufreq/Kconfig | 2 +- drivers/cpufreq/Kconfig.arm | 16 ++ drivers/cpufreq/Makefile | 3 +- drivers/cpufreq/acpi-cpufreq.c | 419 +++++------------------------------- drivers/cpufreq/acpi-cpufreq.h | 68 ++++++ drivers/cpufreq/acpi-cpufreq_arm.c | 88 ++++++++ drivers/cpufreq/acpi-cpufreq_x86.c | 334 ++++++++++++++++++++++++++++ include/linux/acpi.h | 1 + 16 files changed, 604 insertions(+), 373 deletions(-) create mode 100644 drivers/cpufreq/acpi-cpufreq.h create mode 100644 drivers/cpufreq/acpi-cpufreq_arm.c create mode 100644 drivers/cpufreq/acpi-cpufreq_x86.c
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 94cc542..e150e60 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -108,6 +108,13 @@ config IOMMU_HELPER config KERNEL_MODE_NEON def_bool y
+config ARCH_HAS_CPUFREQ
bool
help
Internal node to signify that the ARCH has CPUFREQ support
and that the relevant menu configurations are displayed for
it.
This is already in the mainline now, not required anymore.
source "init/Kconfig"
source "kernel/Kconfig.freezer" @@ -116,6 +123,7 @@ menu "Platform selection"
config ARCH_VEXPRESS bool "ARMv8 software model (Versatile Express)"
select ARCH_HAS_CPUFREQ
This makes no sense, the fast models don't support DVFS.
select ARCH_REQUIRE_GPIOLIB select COMMON_CLK_VERSATILE select POWER_RESET_VEXPRESS
@@ -325,6 +333,10 @@ endmenu
menu "CPU Power Management"
+if ARCH_HAS_CPUFREQ +source "drivers/cpufreq/Kconfig" +endif
source "drivers/cpuidle/Kconfig"
endmenu diff --git a/arch/arm64/include/asm/cpu.h b/arch/arm64/include/asm/cpu.h index 8625eb1..69db3e7 100644 --- a/arch/arm64/include/asm/cpu.h +++ b/arch/arm64/include/asm/cpu.h @@ -20,6 +20,7 @@ struct cpuinfo_arm { #endif };
-DECLARE_PER_CPU(struct cpuinfo_arm, cpu_data); +DECLARE_PER_CPU(struct cpuinfo_arm, cpu_info); +#define cpu_data(cpu) per_cpu(cpu_info, cpu)
This looks like unnecessary change, as you don't use this.
#endif diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h index 50ce951..7196873 100644 --- a/arch/arm64/include/asm/processor.h +++ b/arch/arm64/include/asm/processor.h @@ -47,6 +47,8 @@ #define ARCH_LOW_ADDRESS_LIMIT PHYS_MASK #endif /* __KERNEL__ */
+enum idle_boot_override {IDLE_NO_OVERRIDE=0, IDLE_HALT, IDLE_NOMWAIT,
IDLE_POLL};
This doesn't belong to cpufreq related changes
struct debug_info { /* Have we suspended stepping by a debugger? */ int suspended_step; diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index 1c0a9be..1c985a9 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -86,6 +86,12 @@ void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd); EXPORT_SYMBOL_GPL(arm_pm_restart);
/*
- Idle related variables and functions */ unsigned long
+boot_option_idle_override = IDLE_NO_OVERRIDE; +EXPORT_SYMBOL(boot_option_idle_override);
+/*
Again this doesn't belong to cpufreq related changes.
- This is our default idle handler.
*/ void arch_cpu_idle(void) diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index fecf272..99b973b 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -377,14 +377,15 @@ static int __init arm64_device_init(void) } arch_initcall(arm64_device_init);
-static DEFINE_PER_CPU(struct cpu, cpu_data); +DEFINE_PER_CPU(struct cpu, cpu_info); +EXPORT_PER_CPU_SYMBOL(cpu_info);
Now I think this is introduced just to fix some compilation ?
static int __init topology_init(void) { int i;
for_each_possible_cpu(i) {
struct cpu *cpu = &per_cpu(cpu_data, i);
struct cpu *cpu = &per_cpu(cpu_info, i);
This is want I meant above, you define a macro for this and not use it here ?
cpu->hotpluggable = 1; register_cpu(cpu, i); }
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index cbc5dfc..961211f 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -152,7 +152,7 @@ config ACPI_PROCESSOR tristate "Processor" select THERMAL select CPU_IDLE
depends on X86 || IA64
depends on X86 || IA64 || ARM64 default y help This driver installs ACPI as the idle handler for Linux and uses
diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c index c29c2c3..6a89856 100644 --- a/drivers/acpi/acpi_processor.c +++ b/drivers/acpi/acpi_processor.c @@ -177,6 +177,7 @@ static int acpi_processor_hotadd_init(struct acpi_processor *pr) cpu_maps_update_begin(); cpu_ho\12.36.155.51\jhbirdchoi\Kernel\leg-kerneltplug_begin();
???
+#ifdef CONFIG_X86 ret = acpi_map_lsapic(pr->handle, pr->apic_id, &pr->id); if (ret) goto out; @@ -186,6 +187,7 @@ static int acpi_processor_hotadd_init(struct acpi_processor *pr) acpi_unmap_lsapic(pr->id); goto out; } +#endif
/* * CPU got hot-added, but cpu_data is not initialized yet. Set a
flag @@ -457,8 +459,10 @@ static void acpi_processor_remove(struct acpi_device *device) cpu_hotplug_begin();
/* Remove the CPU. */
+#ifdef CONFIG_X86 arch_unregister_cpu(pr->id);
Why is this X86 specific ?
acpi_unmap_lsapic(pr->id);
+#endif
cpu_hotplug_done(); cpu_maps_update_done();
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 3dca36d..0556a0a 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c
These changes in this file doesn't belong to cpufreq related changes. IMO Moving all such hack separately to another patch makes it easy to review.
[...]
%d\n", diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 3a7202d..6417406 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -228,7 +228,7 @@ source "drivers/cpufreq/Kconfig.x86" endmenu
menu "ARM CPU frequency scaling drivers" -depends on ARM +depends on ARM64
Interesting why are you removing ARM ?
source "drivers/cpufreq/Kconfig.arm" endmenu
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 3129749..ef00a4c 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -16,6 +16,21 @@ config ARM_DT_BL_CPUFREQ This enables probing via DT for Generic CPUfreq driver for ARM big.LITTLE platform. This gets frequency tables from DT.
+config ARM_ACPI_CPUFREQ
tristate "ACPI Processor P-States driver"
depends on ACPI_PROCESSOR
help
This driver adds a CPUFreq driver which utilizes the ACPI
Processor Performance States.
This driver also supports ARM CPUs.
To compile this driver as a module, choose M here: the
module will be called acpi-cpufreq.
For details, take a look at <file:Documentation/cpu-freq/>.
If in doubt, say N.
Do we really need this if we must have single ACPI CPUFreq driver ?
diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c index 18448a7..26bf7112 100644 --- a/drivers/cpufreq/acpi-cpufreq.c +++ b/drivers/cpufreq/acpi-cpufreq.c @@ -42,37 +42,17 @@
#include <acpi/processor.h>
-#include <asm/msr.h> +#include <asm/cpu.h> #include <asm/processor.h> -#include <asm/cpufeature.h>
+#include "acpi-cpufreq.h"
I am skipping all x86 changes for now.
diff --git a/drivers/cpufreq/acpi-cpufreq_arm.c b/drivers/cpufreq/acpi-cpufreq_arm.c new file mode 100644 index 0000000..241c2a1 --- /dev/null +++ b/drivers/cpufreq/acpi-cpufreq_arm.c @@ -0,0 +1,88 @@ +#include <linux/acpi.h> +#include <linux/cpu.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/uaccess.h>
+#include <acpi/processor.h>
+#include <asm/cpu.h> +#include <asm/processor.h>
+#include "acpi-cpufreq.h"
+/* Called via smp_call_function_single(), on the target CPU */ static +void arm_do_drv_read(void *_cmd, struct acpi_cpufreq_data *data) {
struct drv_cmd *cmd = _cmd;
if (data->cpu_feature != SYSTEM_MEMORY_CAPABLE)
return;
This is the core part I would like to understand. So far very few(close to none) ARM SoCs have such memory mapped interface for DVFS. Is this based on you assumption ? Or have you come across such SoC/platforms ?
Even if this is present, we need to think of catering other possible alternate methods we might have to support on ARM SoCs.
acpi_os_read_memory((acpi_physical_address)cmd->addr.mem.addr,
(u64*)&cmd->val,
cmd->addr.mem.bit_width);
Further all you are using is standard System Memory as the Address Space ID, hence IMO this can be part of generic acpi cpufreq driver. I don't see anything ARM specific in Address Space ID being System Memory.
Regards, Sudeep
Hi Sudeep Holla~
Thanks for your comments.
+config ARCH_HAS_CPUFREQ
bool
help
Internal node to signify that the ARCH has CPUFREQ support
and that the relevant menu configurations are displayed for
it.
This is already in the mainline now, not required anymore.
-> Thanks~
endmenu diff --git a/arch/arm64/include/asm/cpu.h b/arch/arm64/include/asm/cpu.h index 8625eb1..69db3e7 100644 --- a/arch/arm64/include/asm/cpu.h +++ b/arch/arm64/include/asm/cpu.h @@ -20,6 +20,7 @@ struct cpuinfo_arm { #endif };
-DECLARE_PER_CPU(struct cpuinfo_arm, cpu_data); +DECLARE_PER_CPU(struct cpuinfo_arm, cpu_info); +#define cpu_data(cpu) per_cpu(cpu_info, cpu)
This looks like unnecessary change, as you don't use this.
+enum idle_boot_override {IDLE_NO_OVERRIDE=0, IDLE_HALT, IDLE_NOMWAIT,
IDLE_POLL};
This doesn't belong to cpufreq related changes
/*
- Idle related variables and functions */ unsigned long
+boot_option_idle_override = IDLE_NO_OVERRIDE; +EXPORT_SYMBOL(boot_option_idle_override);
+/*
Again this doesn't belong to cpufreq related changes.
- This is our default idle handler.
*/ void arch_cpu_idle(void) diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index fecf272..99b973b 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -377,14 +377,15 @@ static int __init arm64_device_init(void) } arch_initcall(arm64_device_init);
-static DEFINE_PER_CPU(struct cpu, cpu_data); +DEFINE_PER_CPU(struct cpu, cpu_info); +EXPORT_PER_CPU_SYMBOL(cpu_info);
Now I think this is introduced just to fix some compilation ?
static int __init topology_init(void) { int i;
for_each_possible_cpu(i) {
struct cpu *cpu = &per_cpu(cpu_data, i);
struct cpu *cpu = &per_cpu(cpu_info, i);
This is want I meant above, you define a macro for this and not use it here ?
-> Yes.~
cpu->hotpluggable = 1; register_cpu(cpu, i); }
6a89856 100644
--- a/drivers/acpi/acpi_processor.c +++ b/drivers/acpi/acpi_processor.c @@ -177,6 +177,7 @@ static int acpi_processor_hotadd_init(struct acpi_processor *pr) cpu_maps_update_begin();
cpu_ho\12.36.155.51\jhbirdchoi\Kernel\leg-kerneltplug_begin();
???
-> My mistake. Sorry
+#ifdef CONFIG_X86 ret = acpi_map_lsapic(pr->handle, pr->apic_id, &pr->id); if (ret) goto out; @@ -186,6 +187,7 @@ static int acpi_processor_hotadd_init(struct acpi_processor *pr) acpi_unmap_lsapic(pr->id); goto out; } +#endif
/* * CPU got hot-added, but cpu_data is not initialized yet.
Set a flag @@ -457,8 +459,10 @@ static void acpi_processor_remove(struct acpi_device *device) cpu_hotplug_begin();
/* Remove the CPU. */
+#ifdef CONFIG_X86 arch_unregister_cpu(pr->id);
Why is this X86 specific ?
-> I used #ifdef for compile, but
acpi_unmap_lsapic(pr->id);
+#endif
cpu_hotplug_done(); cpu_maps_update_done();
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 3dca36d..0556a0a 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c
These changes in this file doesn't belong to cpufreq related changes. IMO Moving all such hack separately to another patch makes it easy to review.
-> Ok, thanks
[...]
%d\n", diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 3a7202d..6417406 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -228,7 +228,7 @@ source "drivers/cpufreq/Kconfig.x86" endmenu
menu "ARM CPU frequency scaling drivers" -depends on ARM +depends on ARM64
Interesting why are you removing ARM ?
-> Let me check.
source "drivers/cpufreq/Kconfig.arm" endmenu
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 3129749..ef00a4c 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -16,6 +16,21 @@ config ARM_DT_BL_CPUFREQ This enables probing via DT for Generic CPUfreq driver for ARM big.LITTLE platform. This gets frequency tables from DT.
+config ARM_ACPI_CPUFREQ
tristate "ACPI Processor P-States driver"
depends on ACPI_PROCESSOR
help
This driver adds a CPUFreq driver which utilizes the ACPI
Processor Performance States.
This driver also supports ARM CPUs.
To compile this driver as a module, choose M here: the
module will be called acpi-cpufreq.
For details, take a look at <file:Documentation/cpu-freq/>.
If in doubt, say N.
Do we really need this if we must have single ACPI CPUFreq driver ?
-> Let me check
diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c index 18448a7..26bf7112 100644 --- a/drivers/cpufreq/acpi-cpufreq.c +++ b/drivers/cpufreq/acpi-cpufreq.c @@ -42,37 +42,17 @@
#include <acpi/processor.h>
-#include <asm/msr.h> +#include <asm/cpu.h> #include <asm/processor.h> -#include <asm/cpufeature.h>
+#include "acpi-cpufreq.h"
I am skipping all x86 changes for now.
diff --git a/drivers/cpufreq/acpi-cpufreq_arm.c b/drivers/cpufreq/acpi-cpufreq_arm.c new file mode 100644 index 0000000..241c2a1 --- /dev/null +++ b/drivers/cpufreq/acpi-cpufreq_arm.c @@ -0,0 +1,88 @@ +#include <linux/acpi.h> +#include <linux/cpu.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/uaccess.h>
+#include <acpi/processor.h>
+#include <asm/cpu.h> +#include <asm/processor.h>
+#include "acpi-cpufreq.h"
+/* Called via smp_call_function_single(), on the target CPU */ static +void arm_do_drv_read(void *_cmd, struct acpi_cpufreq_data *data) {
struct drv_cmd *cmd = _cmd;
if (data->cpu_feature != SYSTEM_MEMORY_CAPABLE)
return;
This is the core part I would like to understand. So far very few(close to none) ARM SoCs have such memory mapped interface for DVFS. Is this based on you assumption ? Or have you come across such SoC/platforms ?
-> Considered our system. To change DVFS, we have to write control value to certain register.(only 1 register) So I added checking SYSTEM_MEMORY_CAPABLE. -> Let me check again
Even if this is present, we need to think of catering other possible alternate methods we might have to support on ARM SoCs.
-> -> Ok. Let me check.
acpi_os_read_memory((acpi_physical_address)cmd->addr.mem.addr,
(u64*)&cmd->val,
cmd->addr.mem.bit_width);
Further all you are using is standard System Memory as the Address Space ID, hence IMO this can be part of generic acpi cpufreq driver. I don't see anything ARM specific in Address Space ID being System Memory.
-> Ok. Let me check.
Thanks for your kind comments.
Best Regards