diff options
| author | Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> | 2016-09-01 16:37:10 -0400 |
|---|---|---|
| committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2016-09-08 17:02:14 -0400 |
| commit | a6cbcdd5ab5f242d49f511127f0a601b71be2cc4 (patch) | |
| tree | 6c7b1fad06bc5dfce0d10ffa9c6903959060df8e | |
| parent | 5448f14698b916e5b04112b104433b90247e01c7 (diff) | |
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>
| -rw-r--r-- | arch/x86/kernel/acpi/Makefile | 1 | ||||
| -rw-r--r-- | arch/x86/kernel/acpi/cppc_msr.c | 58 | ||||
| -rw-r--r-- | drivers/acpi/cppc_acpi.c | 75 |
3 files changed, 121 insertions, 13 deletions
diff --git a/arch/x86/kernel/acpi/Makefile b/arch/x86/kernel/acpi/Makefile index 3242e591fa82..26b78d86f25a 100644 --- a/arch/x86/kernel/acpi/Makefile +++ b/arch/x86/kernel/acpi/Makefile | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | obj-$(CONFIG_ACPI) += boot.o | 1 | obj-$(CONFIG_ACPI) += boot.o |
| 2 | obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup_$(BITS).o | 2 | obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup_$(BITS).o |
| 3 | obj-$(CONFIG_ACPI_APEI) += apei.o | 3 | obj-$(CONFIG_ACPI_APEI) += apei.o |
| 4 | obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_msr.o | ||
| 4 | 5 | ||
| 5 | ifneq ($(CONFIG_ACPI_PROCESSOR),) | 6 | ifneq ($(CONFIG_ACPI_PROCESSOR),) |
| 6 | obj-y += cstate.o | 7 | 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 000000000000..6fb478bf82fd --- /dev/null +++ b/arch/x86/kernel/acpi/cppc_msr.c | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | /* | ||
| 2 | * cppc_msr.c: MSR Interface for CPPC | ||
| 3 | * Copyright (c) 2016, Intel Corporation. | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms and conditions of the GNU General Public License, | ||
| 7 | * version 2, as published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 12 | * more details. | ||
| 13 | * | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <acpi/cppc_acpi.h> | ||
| 17 | #include <asm/msr.h> | ||
| 18 | |||
| 19 | /* Refer to drivers/acpi/cppc_acpi.c for the description of functions */ | ||
| 20 | |||
| 21 | bool cpc_ffh_supported(void) | ||
| 22 | { | ||
| 23 | return true; | ||
| 24 | } | ||
| 25 | |||
| 26 | int cpc_read_ffh(int cpunum, struct cpc_reg *reg, u64 *val) | ||
| 27 | { | ||
| 28 | int err; | ||
| 29 | |||
| 30 | err = rdmsrl_safe_on_cpu(cpunum, reg->address, val); | ||
| 31 | if (!err) { | ||
| 32 | u64 mask = GENMASK_ULL(reg->bit_offset + reg->bit_width - 1, | ||
| 33 | reg->bit_offset); | ||
| 34 | |||
| 35 | *val &= mask; | ||
| 36 | *val >>= reg->bit_offset; | ||
| 37 | } | ||
| 38 | return err; | ||
| 39 | } | ||
| 40 | |||
| 41 | int cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val) | ||
| 42 | { | ||
| 43 | u64 rd_val; | ||
| 44 | int err; | ||
| 45 | |||
| 46 | err = rdmsrl_safe_on_cpu(cpunum, reg->address, &rd_val); | ||
| 47 | if (!err) { | ||
| 48 | u64 mask = GENMASK_ULL(reg->bit_offset + reg->bit_width - 1, | ||
| 49 | reg->bit_offset); | ||
| 50 | |||
| 51 | val <<= reg->bit_offset; | ||
| 52 | val &= mask; | ||
| 53 | rd_val &= ~mask; | ||
| 54 | rd_val |= val; | ||
| 55 | err = wrmsrl_safe_on_cpu(cpunum, reg->address, rd_val); | ||
| 56 | } | ||
| 57 | return err; | ||
| 58 | } | ||
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index ed58883d35ee..715fe8001d59 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) | |||
| 579 | return 0; | 579 | return 0; |
| 580 | } | 580 | } |
| 581 | 581 | ||
| 582 | /** | ||
| 583 | * cpc_ffh_supported() - check if FFH reading supported | ||
| 584 | * | ||
| 585 | * Check if the architecture has support for functional fixed hardware | ||
| 586 | * read/write capability. | ||
| 587 | * | ||
| 588 | * Return: true for supported, false for not supported | ||
| 589 | */ | ||
| 590 | bool __weak cpc_ffh_supported(void) | ||
| 591 | { | ||
| 592 | return false; | ||
| 593 | } | ||
| 594 | |||
| 582 | /* | 595 | /* |
| 583 | * An example CPC table looks like the following. | 596 | * An example CPC table looks like the following. |
| 584 | * | 597 | * |
| @@ -728,9 +741,11 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) | |||
| 728 | cpc_ptr->cpc_regs[i-2].sys_mem_vaddr = addr; | 741 | cpc_ptr->cpc_regs[i-2].sys_mem_vaddr = addr; |
| 729 | } | 742 | } |
| 730 | } else { | 743 | } else { |
| 731 | /* Support only PCC and SYS MEM type regs */ | 744 | if (gas_t->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE || !cpc_ffh_supported()) { |
| 732 | pr_debug("Unsupported register type: %d\n", gas_t->space_id); | 745 | /* Support only PCC ,SYS MEM and FFH type regs */ |
| 733 | goto out_free; | 746 | pr_debug("Unsupported register type: %d\n", gas_t->space_id); |
| 747 | goto out_free; | ||
| 748 | } | ||
| 734 | } | 749 | } |
| 735 | 750 | ||
| 736 | cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_BUFFER; | 751 | cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_BUFFER; |
| @@ -819,13 +834,43 @@ void acpi_cppc_processor_exit(struct acpi_processor *pr) | |||
| 819 | } | 834 | } |
| 820 | EXPORT_SYMBOL_GPL(acpi_cppc_processor_exit); | 835 | EXPORT_SYMBOL_GPL(acpi_cppc_processor_exit); |
| 821 | 836 | ||
| 837 | /** | ||
| 838 | * cpc_read_ffh() - Read FFH register | ||
| 839 | * @cpunum: cpu number to read | ||
| 840 | * @reg: cppc register information | ||
| 841 | * @val: place holder for return value | ||
| 842 | * | ||
| 843 | * Read bit_width bits from a specified address and bit_offset | ||
| 844 | * | ||
| 845 | * Return: 0 for success and error code | ||
| 846 | */ | ||
| 847 | int __weak cpc_read_ffh(int cpunum, struct cpc_reg *reg, u64 *val) | ||
| 848 | { | ||
| 849 | return -ENOTSUPP; | ||
| 850 | } | ||
| 851 | |||
| 852 | /** | ||
| 853 | * cpc_write_ffh() - Write FFH register | ||
| 854 | * @cpunum: cpu number to write | ||
| 855 | * @reg: cppc register information | ||
| 856 | * @val: value to write | ||
| 857 | * | ||
| 858 | * Write value of bit_width bits to a specified address and bit_offset | ||
| 859 | * | ||
| 860 | * Return: 0 for success and error code | ||
| 861 | */ | ||
| 862 | int __weak cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val) | ||
| 863 | { | ||
| 864 | return -ENOTSUPP; | ||
| 865 | } | ||
| 866 | |||
| 822 | /* | 867 | /* |
| 823 | * Since cpc_read and cpc_write are called while holding pcc_lock, it should be | 868 | * Since cpc_read and cpc_write are called while holding pcc_lock, it should be |
| 824 | * as fast as possible. We have already mapped the PCC subspace during init, so | 869 | * as fast as possible. We have already mapped the PCC subspace during init, so |
| 825 | * we can directly write to it. | 870 | * we can directly write to it. |
| 826 | */ | 871 | */ |
| 827 | 872 | ||
| 828 | static int cpc_read(struct cpc_register_resource *reg_res, u64 *val) | 873 | static int cpc_read(int cpu, struct cpc_register_resource *reg_res, u64 *val) |
| 829 | { | 874 | { |
| 830 | int ret_val = 0; | 875 | int ret_val = 0; |
| 831 | void __iomem *vaddr = 0; | 876 | void __iomem *vaddr = 0; |
| @@ -841,6 +886,8 @@ static int cpc_read(struct cpc_register_resource *reg_res, u64 *val) | |||
| 841 | vaddr = GET_PCC_VADDR(reg->address); | 886 | vaddr = GET_PCC_VADDR(reg->address); |
| 842 | else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) | 887 | else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) |
| 843 | vaddr = reg_res->sys_mem_vaddr; | 888 | vaddr = reg_res->sys_mem_vaddr; |
| 889 | else if (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) | ||
| 890 | return cpc_read_ffh(cpu, reg, val); | ||
| 844 | else | 891 | else |
| 845 | return acpi_os_read_memory((acpi_physical_address)reg->address, | 892 | return acpi_os_read_memory((acpi_physical_address)reg->address, |
| 846 | val, reg->bit_width); | 893 | val, reg->bit_width); |
| @@ -867,7 +914,7 @@ static int cpc_read(struct cpc_register_resource *reg_res, u64 *val) | |||
| 867 | return ret_val; | 914 | return ret_val; |
| 868 | } | 915 | } |
| 869 | 916 | ||
| 870 | static int cpc_write(struct cpc_register_resource *reg_res, u64 val) | 917 | static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val) |
| 871 | { | 918 | { |
| 872 | int ret_val = 0; | 919 | int ret_val = 0; |
| 873 | void __iomem *vaddr = 0; | 920 | void __iomem *vaddr = 0; |
| @@ -877,6 +924,8 @@ static int cpc_write(struct cpc_register_resource *reg_res, u64 val) | |||
| 877 | vaddr = GET_PCC_VADDR(reg->address); | 924 | vaddr = GET_PCC_VADDR(reg->address); |
| 878 | else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) | 925 | else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) |
| 879 | vaddr = reg_res->sys_mem_vaddr; | 926 | vaddr = reg_res->sys_mem_vaddr; |
| 927 | else if (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) | ||
| 928 | return cpc_write_ffh(cpu, reg, val); | ||
| 880 | else | 929 | else |
| 881 | return acpi_os_write_memory((acpi_physical_address)reg->address, | 930 | return acpi_os_write_memory((acpi_physical_address)reg->address, |
| 882 | val, reg->bit_width); | 931 | val, reg->bit_width); |
| @@ -941,13 +990,13 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps) | |||
| 941 | } | 990 | } |
| 942 | } | 991 | } |
| 943 | 992 | ||
| 944 | cpc_read(highest_reg, &high); | 993 | cpc_read(cpunum, highest_reg, &high); |
| 945 | perf_caps->highest_perf = high; | 994 | perf_caps->highest_perf = high; |
| 946 | 995 | ||
| 947 | c | ||
