Commit 62839e2d authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

Merge branch 'acpi-processor'

* acpi-processor:
  ACPI / CPPC: Fix potential memory leak
  ACPI / CPPC: signedness bug in register_pcc_channel()
  ACPI: Allow selection of the ACPI processor driver for ARM64
  CPPC: Probe for CPPC tables for each ACPI Processor object
  ACPI: Add weak routines for ACPI CPU Hotplug
  ACPI / CPPC: Add a CPUFreq driver for use with CPPC
  ACPI: Introduce CPU performance controls using CPPC
parents 5b9ddd0d 4219853a
......@@ -206,11 +206,25 @@ config ACPI_PROCESSOR_IDLE
bool
select CPU_IDLE
config ACPI_CPPC_LIB
bool
depends on ACPI_PROCESSOR
depends on !ACPI_CPU_FREQ_PSS
select MAILBOX
select PCC
help
If this option is enabled, this file implements common functionality
to parse CPPC tables as described in the ACPI 5.1+ spec. The
routines implemented are meant to be used by other
drivers to control CPU performance using CPPC semantics.
If your platform does not support CPPC in firmware,
leave this option disabled.
config ACPI_PROCESSOR
tristate "Processor"
depends on X86 || IA64
select ACPI_PROCESSOR_IDLE
select ACPI_CPU_FREQ_PSS
depends on X86 || IA64 || ARM64
select ACPI_PROCESSOR_IDLE if X86 || IA64
select ACPI_CPU_FREQ_PSS if X86 || IA64
default y
help
This driver adds support for the ACPI Processor package. It is required
......
......@@ -78,6 +78,7 @@ obj-$(CONFIG_ACPI_HED) += hed.o
obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o
obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o
obj-$(CONFIG_ACPI_BGRT) += bgrt.o
obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_acpi.o
# processor has its own "processor." module_param namespace
processor-y := processor_driver.o
......
......@@ -164,6 +164,24 @@ static int acpi_processor_errata(void)
-------------------------------------------------------------------------- */
#ifdef CONFIG_ACPI_HOTPLUG_CPU
int __weak acpi_map_cpu(acpi_handle handle,
phys_cpuid_t physid, int *pcpu)
{
return -ENODEV;
}
int __weak acpi_unmap_cpu(int cpu)
{
return -ENODEV;
}
int __weak arch_register_cpu(int cpu)
{
return -ENODEV;
}
void __weak arch_unregister_cpu(int cpu) {}
static int acpi_processor_hotadd_init(struct acpi_processor *pr)
{
unsigned long long sta;
......
This diff is collapsed.
......@@ -242,6 +242,10 @@ static int __acpi_processor_start(struct acpi_device *device)
if (pr->flags.need_hotplug_init)
return 0;
result = acpi_cppc_processor_probe(pr);
if (result)
return -ENODEV;
if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver)
acpi_processor_power_init(pr);
......@@ -287,6 +291,8 @@ static int acpi_processor_stop(struct device *dev)
acpi_pss_perf_exit(pr, device);
acpi_cppc_processor_exit(pr);
return 0;
}
......
......@@ -227,3 +227,20 @@ config ARM_PXA2xx_CPUFREQ
This add the CPUFreq driver support for Intel PXA2xx SOCs.
If in doubt, say N.
config ACPI_CPPC_CPUFREQ
tristate "CPUFreq driver based on the ACPI CPPC spec"
depends on ACPI
select ACPI_CPPC_LIB
default n
help
This adds a CPUFreq driver which uses CPPC methods
as described in the ACPIv5.1 spec. CPPC stands for
Collaborative Processor Performance Controls. It
is based on an abstract continuous scale of CPU
performance values which allows the remote power
processor to flexibly optimize for power and
performance. CPPC relies on power management firmware
support for its operation.
If in doubt, say N.
......@@ -76,6 +76,8 @@ obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o
obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o
obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o
obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o
obj-$(CONFIG_ACPI_CPPC_CPUFREQ) += cppc_cpufreq.o
##################################################################################
# PowerPC platform drivers
......
/*
* CPPC (Collaborative Processor Performance Control) driver for
* interfacing with the CPUfreq layer and governors. See
* cppc_acpi.c for CPPC specific methods.
*
* (C) Copyright 2014, 2015 Linaro Ltd.
* Author: Ashwin Chaugule <ashwin.chaugule@linaro.org>
*
* 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; version 2
* of the License.
*/
#define pr_fmt(fmt) "CPPC Cpufreq:" fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/cpu.h>
#include <linux/cpufreq.h>
#include <linux/vmalloc.h>
#include <acpi/cppc_acpi.h>
/*
* These structs contain information parsed from per CPU
* ACPI _CPC structures.
* e.g. For each CPU the highest, lowest supported
* performance capabilities, desired performance level
* requested etc.
*/
static struct cpudata **all_cpu_data;
static int cppc_cpufreq_set_target(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation)
{
struct cpudata *cpu;
struct cpufreq_freqs freqs;
int ret = 0;
cpu = all_cpu_data[policy->cpu];
cpu->perf_ctrls.desired_perf = target_freq;
freqs.old = policy->cur;
freqs.new = target_freq;
cpufreq_freq_transition_begin(policy, &freqs);
ret = cppc_set_perf(cpu->cpu, &cpu->perf_ctrls);
cpufreq_freq_transition_end(policy, &freqs, ret != 0);
if (ret)
pr_debug("Failed to set target on CPU:%d. ret:%d\n",
cpu->cpu, ret);
return ret;
}
static int cppc_verify_policy(struct cpufreq_policy *policy)
{
cpufreq_verify_within_cpu_limits(policy);
return 0;
}
static void cppc_cpufreq_stop_cpu(struct cpufreq_policy *policy)
{
int cpu_num = policy->cpu;
struct cpudata *cpu = all_cpu_data[cpu_num];
int ret;
cpu->perf_ctrls.desired_perf = cpu->perf_caps.lowest_perf;
ret = cppc_set_perf(cpu_num, &cpu->perf_ctrls);
if (ret)
pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n",
cpu->perf_caps.lowest_perf, cpu_num, ret);
}
static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
struct cpudata *cpu;
unsigned int cpu_num = policy->cpu;
int ret = 0;
cpu = all_cpu_data[policy->cpu];
cpu->cpu = cpu_num;
ret = cppc_get_perf_caps(policy->cpu, &cpu->perf_caps);
if (ret) {
pr_debug("Err reading CPU%d perf capabilities. ret:%d\n",
cpu_num, ret);
return ret;
}
policy->min = cpu->perf_caps.lowest_perf;
policy->max = cpu->perf_caps.highest_perf;
policy->cpuinfo.min_freq = policy->min;
policy->cpuinfo.max_freq = policy->max;
if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY)
cpumask_copy(policy->cpus, cpu->shared_cpu_map);
else {
/* Support only SW_ANY for now. */
pr_debug("Unsupported CPU co-ord type\n");
return -EFAULT;
}
cpumask_set_cpu(policy->cpu, policy->cpus);
cpu->cur_policy = policy;
/* Set policy->cur to max now. The governors will adjust later. */
policy->cur = cpu->perf_ctrls.desired_perf = cpu->perf_caps.highest_perf;
ret = cppc_set_perf(cpu_num, &cpu->perf_ctrls);
if (ret)
pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n",
cpu->perf_caps.highest_perf, cpu_num, ret);
return ret;
}
static struct cpufreq_driver cppc_cpufreq_driver = {
.flags = CPUFREQ_CONST_LOOPS,
.verify = cppc_verify_policy,
.target = cppc_cpufreq_set_target,
.init = cppc_cpufreq_cpu_init,
.stop_cpu = cppc_cpufreq_stop_cpu,
.name = "cppc_cpufreq",
};
static int __init cppc_cpufreq_init(void)
{
int i, ret = 0;
struct cpudata *cpu;
if (acpi_disabled)
return -ENODEV;
all_cpu_data = kzalloc(sizeof(void *) * num_possible_cpus(), GFP_KERNEL);
if (!all_cpu_data)
return -ENOMEM;
for_each_possible_cpu(i) {
all_cpu_data[i] = kzalloc(sizeof(struct cpudata), GFP_KERNEL);
if (!all_cpu_data[i])
goto out;
cpu = all_cpu_data[i];
if (!zalloc_cpumask_var(&cpu->shared_cpu_map, GFP_KERNEL))
goto out;
}
ret = acpi_get_psd_map(all_cpu_data);
if (ret) {
pr_debug("Error parsing PSD data. Aborting cpufreq registration.\n");
goto out;
}
ret = cpufreq_register_driver(&cppc_cpufreq_driver);
if (ret)
goto out;
return ret;
out:
for_each_possible_cpu(i)
if (all_cpu_data[i])
kfree(all_cpu_data[i]);
kfree(all_cpu_data);
return -ENODEV;
}
late_initcall(cppc_cpufreq_init);
/*
* CPPC (Collaborative Processor Performance Control) methods used
* by CPUfreq drivers.
*
* (C) Copyright 2014, 2015 Linaro Ltd.
* Author: Ashwin Chaugule <ashwin.chaugule@linaro.org>
*
* 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; version 2
* of the License.
*/
#ifndef _CPPC_ACPI_H
#define _CPPC_ACPI_H
#include <linux/acpi.h>
#include <linux/mailbox_controller.h>
#include <linux/mailbox_client.h>
#include <linux/types.h>
#include <acpi/processor.h>
/* Only support CPPCv2 for now. */
#define CPPC_NUM_ENT 21
#define CPPC_REV 2
#define PCC_CMD_COMPLETE 1
#define MAX_CPC_REG_ENT 19
/* CPPC specific PCC commands. */
#define CMD_READ 0
#define CMD_WRITE 1
/* Each register has the folowing format. */
struct cpc_reg {
u8 descriptor;
u16 length;
u8 space_id;
u8 bit_width;
u8 bit_offset;
u8 access_width;
u64 __iomem address;
} __packed;
/*
* Each entry in the CPC table is either
* of type ACPI_TYPE_BUFFER or
* ACPI_TYPE_INTEGER.
*/
struct cpc_register_resource {
acpi_object_type type;
union {
struct cpc_reg reg;
u64 int_value;
} cpc_entry;
};
/* Container to hold the CPC details for each CPU */
struct cpc_desc {
int num_entries;
int version;
int cpu_id;
struct cpc_register_resource cpc_regs[MAX_CPC_REG_ENT];
struct acpi_psd_package domain_info;
};
/* These are indexes into the per-cpu cpc_regs[]. Order is important. */
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,
};
/*
* Categorization of registers as described
* in the ACPI v.5.1 spec.
* XXX: Only filling up ones which are used by governors
* today.
*/
struct cppc_perf_caps {
u32 highest_perf;
u32 nominal_perf;
u32 reference_perf;
u32 lowest_perf;
};
struct cppc_perf_ctrls {
u32 max_perf;
u32 min_perf;
u32 desired_perf;
};
struct cppc_perf_fb_ctrs {
u64 reference;
u64 prev_reference;
u64 delivered;
u64 prev_delivered;
};
/* Per CPU container for runtime CPPC management. */
struct cpudata {
int cpu;
struct cppc_perf_caps perf_caps;
struct cppc_perf_ctrls perf_ctrls;
struct cppc_perf_fb_ctrs perf_fb_ctrs;
struct cpufreq_policy *cur_policy;
unsigned int shared_type;
cpumask_var_t shared_cpu_map;
};
extern int cppc_get_perf_ctrs(int cpu, struct cppc_perf_fb_ctrs *perf_fb_ctrs);
extern int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls);
extern int cppc_get_perf_caps(int cpu, struct cppc_perf_caps *caps);
extern int acpi_get_psd_map(struct cpudata **);
/* Methods to interact with the PCC mailbox controller. */
extern struct mbox_chan *
pcc_mbox_request_channel(struct mbox_client *, unsigned int);
extern int mbox_send_message(struct mbox_chan *chan, void *mssg);
#endif /* _CPPC_ACPI_H*/
......@@ -311,6 +311,20 @@ phys_cpuid_t acpi_get_phys_id(acpi_handle, int type, u32 acpi_id);
int acpi_map_cpuid(phys_cpuid_t phys_id, u32 acpi_id);
int acpi_get_cpuid(acpi_handle, int type, u32 acpi_id);
#ifdef CONFIG_ACPI_CPPC_LIB
extern int acpi_cppc_processor_probe(struct acpi_processor *pr);
extern void acpi_cppc_processor_exit(struct acpi_processor *pr);
#else
static inline int acpi_cppc_processor_probe(struct acpi_processor *pr)
{
return 0;
}
static inline void acpi_cppc_processor_exit(struct acpi_processor *pr)
{
return;
}
#endif /* CONFIG_ACPI_CPPC_LIB */
/* in processor_pdc.c */
void acpi_processor_set_pdc(acpi_handle handle);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment