mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-09 15:29:16 +00:00
ACPI / CPPC: Add support for functional fixed hardware address
The CPPC registers can also be accessed via functional fixed hardware addresse(FFH) in X86. Add support by modifying cpc_read and cpc_write to be able to read/write MSRs on x86 platform on per cpu basis. Also with this change, acpi_cppc_processor_probe doesn't bail out if address space id is not equal to PCC or memory address space and FFH is supported on the system. Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
parent
5448f14698
commit
a6cbcdd5ab
@ -1,6 +1,7 @@
|
||||
obj-$(CONFIG_ACPI) += boot.o
|
||||
obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup_$(BITS).o
|
||||
obj-$(CONFIG_ACPI_APEI) += apei.o
|
||||
obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_msr.o
|
||||
|
||||
ifneq ($(CONFIG_ACPI_PROCESSOR),)
|
||||
obj-y += cstate.o
|
||||
|
58
arch/x86/kernel/acpi/cppc_msr.c
Normal file
58
arch/x86/kernel/acpi/cppc_msr.c
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* cppc_msr.c: MSR Interface for CPPC
|
||||
* Copyright (c) 2016, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <acpi/cppc_acpi.h>
|
||||
#include <asm/msr.h>
|
||||
|
||||
/* Refer to drivers/acpi/cppc_acpi.c for the description of functions */
|
||||
|
||||
bool cpc_ffh_supported(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int cpc_read_ffh(int cpunum, struct cpc_reg *reg, u64 *val)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = rdmsrl_safe_on_cpu(cpunum, reg->address, val);
|
||||
if (!err) {
|
||||
u64 mask = GENMASK_ULL(reg->bit_offset + reg->bit_width - 1,
|
||||
reg->bit_offset);
|
||||
|
||||
*val &= mask;
|
||||
*val >>= reg->bit_offset;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val)
|
||||
{
|
||||
u64 rd_val;
|
||||
int err;
|
||||
|
||||
err = rdmsrl_safe_on_cpu(cpunum, reg->address, &rd_val);
|
||||
if (!err) {
|
||||
u64 mask = GENMASK_ULL(reg->bit_offset + reg->bit_width - 1,
|
||||
reg->bit_offset);
|
||||
|
||||
val <<= reg->bit_offset;
|
||||
val &= mask;
|
||||
rd_val &= ~mask;
|
||||
rd_val |= val;
|
||||
err = wrmsrl_safe_on_cpu(cpunum, reg->address, rd_val);
|
||||
}
|
||||
return err;
|
||||
}
|
@ -579,6 +579,19 @@ static int register_pcc_channel(int pcc_subspace_idx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cpc_ffh_supported() - check if FFH reading supported
|
||||
*
|
||||
* Check if the architecture has support for functional fixed hardware
|
||||
* read/write capability.
|
||||
*
|
||||
* Return: true for supported, false for not supported
|
||||
*/
|
||||
bool __weak cpc_ffh_supported(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* An example CPC table looks like the following.
|
||||
*
|
||||
@ -728,10 +741,12 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
|
||||
cpc_ptr->cpc_regs[i-2].sys_mem_vaddr = addr;
|
||||
}
|
||||
} else {
|
||||
/* Support only PCC and SYS MEM type regs */
|
||||
if (gas_t->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE || !cpc_ffh_supported()) {
|
||||
/* Support only PCC ,SYS MEM and FFH type regs */
|
||||
pr_debug("Unsupported register type: %d\n", gas_t->space_id);
|
||||
goto out_free;
|
||||
}
|
||||
}
|
||||
|
||||
cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_BUFFER;
|
||||
memcpy(&cpc_ptr->cpc_regs[i-2].cpc_entry.reg, gas_t, sizeof(*gas_t));
|
||||
@ -819,13 +834,43 @@ void acpi_cppc_processor_exit(struct acpi_processor *pr)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_cppc_processor_exit);
|
||||
|
||||
/**
|
||||
* cpc_read_ffh() - Read FFH register
|
||||
* @cpunum: cpu number to read
|
||||
* @reg: cppc register information
|
||||
* @val: place holder for return value
|
||||
*
|
||||
* Read bit_width bits from a specified address and bit_offset
|
||||
*
|
||||
* Return: 0 for success and error code
|
||||
*/
|
||||
int __weak cpc_read_ffh(int cpunum, struct cpc_reg *reg, u64 *val)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
/**
|
||||
* cpc_write_ffh() - Write FFH register
|
||||
* @cpunum: cpu number to write
|
||||
* @reg: cppc register information
|
||||
* @val: value to write
|
||||
*
|
||||
* Write value of bit_width bits to a specified address and bit_offset
|
||||
*
|
||||
* Return: 0 for success and error code
|
||||
*/
|
||||
int __weak cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
/*
|
||||
* Since cpc_read and cpc_write are called while holding pcc_lock, it should be
|
||||
* as fast as possible. We have already mapped the PCC subspace during init, so
|
||||
* we can directly write to it.
|
||||
*/
|
||||
|
||||
static int cpc_read(struct cpc_register_resource *reg_res, u64 *val)
|
||||
static int cpc_read(int cpu, struct cpc_register_resource *reg_res, u64 *val)
|
||||
{
|
||||
int ret_val = 0;
|
||||
void __iomem *vaddr = 0;
|
||||
@ -841,6 +886,8 @@ static int cpc_read(struct cpc_register_resource *reg_res, u64 *val)
|
||||
vaddr = GET_PCC_VADDR(reg->address);
|
||||
else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
|
||||
vaddr = reg_res->sys_mem_vaddr;
|
||||
else if (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE)
|
||||
return cpc_read_ffh(cpu, reg, val);
|
||||
else
|
||||
return acpi_os_read_memory((acpi_physical_address)reg->address,
|
||||
val, reg->bit_width);
|
||||
@ -867,7 +914,7 @@ static int cpc_read(struct cpc_register_resource *reg_res, u64 *val)
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static int cpc_write(struct cpc_register_resource *reg_res, u64 val)
|
||||
static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
|
||||
{
|
||||
int ret_val = 0;
|
||||
void __iomem *vaddr = 0;
|
||||
@ -877,6 +924,8 @@ static int cpc_write(struct cpc_register_resource *reg_res, u64 val)
|
||||
vaddr = GET_PCC_VADDR(reg->address);
|
||||
else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
|
||||
vaddr = reg_res->sys_mem_vaddr;
|
||||
else if (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE)
|
||||
return cpc_write_ffh(cpu, reg, val);
|
||||
else
|
||||
return acpi_os_write_memory((acpi_physical_address)reg->address,
|
||||
val, reg->bit_width);
|
||||
@ -941,13 +990,13 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
|
||||
}
|
||||
}
|
||||
|
||||
cpc_read(highest_reg, &high);
|
||||
cpc_read(cpunum, highest_reg, &high);
|
||||
perf_caps->highest_perf = high;
|
||||
|
||||
cpc_read(lowest_reg, &low);
|
||||
cpc_read(cpunum, lowest_reg, &low);
|
||||
perf_caps->lowest_perf = low;
|
||||
|
||||
cpc_read(nom_perf, &nom);
|
||||
cpc_read(cpunum, nom_perf, &nom);
|
||||
perf_caps->nominal_perf = nom;
|
||||
|
||||
if (!high || !low || !nom)
|
||||
@ -1004,9 +1053,9 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
|
||||
}
|
||||
}
|
||||
|
||||
cpc_read(delivered_reg, &delivered);
|
||||
cpc_read(reference_reg, &reference);
|
||||
cpc_read(ref_perf_reg, &ref_perf);
|
||||
cpc_read(cpunum, delivered_reg, &delivered);
|
||||
cpc_read(cpunum, reference_reg, &reference);
|
||||
cpc_read(cpunum, ref_perf_reg, &ref_perf);
|
||||
|
||||
/*
|
||||
* Per spec, if ctr_wrap_time optional register is unsupported, then the
|
||||
@ -1015,7 +1064,7 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
|
||||
*/
|
||||
ctr_wrap_time = (u64)(~((u64)0));
|
||||
if (CPC_SUPPORTED(ctr_wrap_reg))
|
||||
cpc_read(ctr_wrap_reg, &ctr_wrap_time);
|
||||
cpc_read(cpunum, ctr_wrap_reg, &ctr_wrap_time);
|
||||
|
||||
if (!delivered || !reference || !ref_perf) {
|
||||
ret = -EFAULT;
|
||||
@ -1082,7 +1131,7 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
|
||||
* Skip writing MIN/MAX until Linux knows how to come up with
|
||||
* useful values.
|
||||
*/
|
||||
cpc_write(desired_reg, perf_ctrls->desired_perf);
|
||||
cpc_write(cpu, desired_reg, perf_ctrls->desired_perf);
|
||||
|
||||
if (CPC_IN_PCC(desired_reg))
|
||||
up_read(&pcc_data.pcc_lock); /* END Phase-I */
|
||||
|
Loading…
x
Reference in New Issue
Block a user