From patchwork Thu Sep 1 20:37:10 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: srinivas pandruvada X-Patchwork-Id: 9309661 X-Patchwork-Delegate: rjw@sisk.pl Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 6B342607D6 for ; Thu, 1 Sep 2016 21:20:02 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 30842293E2 for ; Thu, 1 Sep 2016 21:20:02 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 2216729548; Thu, 1 Sep 2016 21:20:02 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DDD09294F3 for ; Thu, 1 Sep 2016 21:20:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932228AbcIAVTy (ORCPT ); Thu, 1 Sep 2016 17:19:54 -0400 Received: from mga07.intel.com ([134.134.136.100]:26017 "EHLO mga07.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754505AbcIAVTU (ORCPT ); Thu, 1 Sep 2016 17:19:20 -0400 Received: from orsmga003.jf.intel.com ([10.7.209.27]) by orsmga105.jf.intel.com with ESMTP; 01 Sep 2016 13:37:12 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.30,268,1470726000"; d="scan'208";a="874221499" Received: from spandruv-desk.jf.intel.com ([10.54.75.13]) by orsmga003.jf.intel.com with ESMTP; 01 Sep 2016 13:37:12 -0700 From: Srinivas Pandruvada To: rjw@rjwysocki.net, tglx@linutronix.de, mingo@redhat.com, bp@suse.de, peterz@infradead.org Cc: x86@kernel.org, linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, linux-acpi@vger.kernel.org, tim.c.chen@linux.intel.com, alexey.klimov@arm.com, Srinivas Pandruvada Subject: [PATCH v2 3/4] acpi: cppc: Add support for functional fixed hardware address Date: Thu, 1 Sep 2016 13:37:10 -0700 Message-Id: <1472762231-88923-4-git-send-email-srinivas.pandruvada@linux.intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1472762231-88923-1-git-send-email-srinivas.pandruvada@linux.intel.com> References: <1472762231-88923-1-git-send-email-srinivas.pandruvada@linux.intel.com> Sender: linux-acpi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-acpi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 --- arch/x86/kernel/acpi/Makefile | 1 + arch/x86/kernel/acpi/cppc_msr.c | 58 +++++++++++++++++++++++++++++++ drivers/acpi/cppc_acpi.c | 75 ++++++++++++++++++++++++++++++++++------- 3 files changed, 121 insertions(+), 13 deletions(-) create mode 100644 arch/x86/kernel/acpi/cppc_msr.c diff --git a/arch/x86/kernel/acpi/Makefile b/arch/x86/kernel/acpi/Makefile index 3242e59..26b78d8 100644 --- a/arch/x86/kernel/acpi/Makefile +++ b/arch/x86/kernel/acpi/Makefile @@ -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 diff --git a/arch/x86/kernel/acpi/cppc_msr.c b/arch/x86/kernel/acpi/cppc_msr.c new file mode 100644 index 0000000..6fb478b --- /dev/null +++ b/arch/x86/kernel/acpi/cppc_msr.c @@ -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 +#include + +/* 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; +} diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index ed58883..715fe80 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -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,9 +741,11 @@ 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 */ - pr_debug("Unsupported register type: %d\n", gas_t->space_id); - goto out_free; + 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; @@ -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 */