Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only 2 : : 3 : : /* 4 : : * acpi_lpit.c - LPIT table processing functions 5 : : * 6 : : * Copyright (C) 2017 Intel Corporation. All rights reserved. 7 : : */ 8 : : 9 : : #include <linux/cpu.h> 10 : : #include <linux/acpi.h> 11 : : #include <asm/msr.h> 12 : : #include <asm/tsc.h> 13 : : 14 : : struct lpit_residency_info { 15 : : struct acpi_generic_address gaddr; 16 : : u64 frequency; 17 : : void __iomem *iomem_addr; 18 : : }; 19 : : 20 : : /* Storage for an memory mapped and FFH based entries */ 21 : : static struct lpit_residency_info residency_info_mem; 22 : : static struct lpit_residency_info residency_info_ffh; 23 : : 24 : 0 : static int lpit_read_residency_counter_us(u64 *counter, bool io_mem) 25 : : { 26 : 0 : int err; 27 : : 28 [ # # ]: 0 : if (io_mem) { 29 : 0 : u64 count = 0; 30 : 0 : int error; 31 : : 32 : 0 : error = acpi_os_read_iomem(residency_info_mem.iomem_addr, &count, 33 : 0 : residency_info_mem.gaddr.bit_width); 34 [ # # ]: 0 : if (error) 35 : : return error; 36 : : 37 : 0 : *counter = div64_u64(count * 1000000ULL, residency_info_mem.frequency); 38 : 0 : return 0; 39 : : } 40 : : 41 : 0 : err = rdmsrl_safe(residency_info_ffh.gaddr.address, counter); 42 [ # # ]: 0 : if (!err) { 43 : 0 : u64 mask = GENMASK_ULL(residency_info_ffh.gaddr.bit_offset + 44 : : residency_info_ffh.gaddr. bit_width - 1, 45 : : residency_info_ffh.gaddr.bit_offset); 46 : : 47 : 0 : *counter &= mask; 48 : 0 : *counter >>= residency_info_ffh.gaddr.bit_offset; 49 : 0 : *counter = div64_u64(*counter * 1000000ULL, residency_info_ffh.frequency); 50 : 0 : return 0; 51 : : } 52 : : 53 : : return -ENODATA; 54 : : } 55 : : 56 : 0 : static ssize_t low_power_idle_system_residency_us_show(struct device *dev, 57 : : struct device_attribute *attr, 58 : : char *buf) 59 : : { 60 : 0 : u64 counter; 61 : 0 : int ret; 62 : : 63 : 0 : ret = lpit_read_residency_counter_us(&counter, true); 64 [ # # ]: 0 : if (ret) 65 : 0 : return ret; 66 : : 67 : 0 : return sprintf(buf, "%llu\n", counter); 68 : : } 69 : : static DEVICE_ATTR_RO(low_power_idle_system_residency_us); 70 : : 71 : 0 : static ssize_t low_power_idle_cpu_residency_us_show(struct device *dev, 72 : : struct device_attribute *attr, 73 : : char *buf) 74 : : { 75 : 0 : u64 counter; 76 : 0 : int ret; 77 : : 78 : 0 : ret = lpit_read_residency_counter_us(&counter, false); 79 [ # # ]: 0 : if (ret) 80 : 0 : return ret; 81 : : 82 : 0 : return sprintf(buf, "%llu\n", counter); 83 : : } 84 : : static DEVICE_ATTR_RO(low_power_idle_cpu_residency_us); 85 : : 86 : 0 : int lpit_read_residency_count_address(u64 *address) 87 : : { 88 [ # # ]: 0 : if (!residency_info_mem.gaddr.address) 89 : : return -EINVAL; 90 : : 91 : 0 : *address = residency_info_mem.gaddr.address; 92 : : 93 : 0 : return 0; 94 : : } 95 : : EXPORT_SYMBOL_GPL(lpit_read_residency_count_address); 96 : : 97 : 0 : static void lpit_update_residency(struct lpit_residency_info *info, 98 : : struct acpi_lpit_native *lpit_native) 99 : : { 100 : 0 : info->frequency = lpit_native->counter_frequency ? 101 [ # # ]: 0 : lpit_native->counter_frequency : tsc_khz * 1000; 102 [ # # ]: 0 : if (!info->frequency) 103 : 0 : info->frequency = 1; 104 : : 105 : 0 : info->gaddr = lpit_native->residency_counter; 106 [ # # ]: 0 : if (info->gaddr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { 107 : 0 : info->iomem_addr = ioremap(info->gaddr.address, 108 : 0 : info->gaddr.bit_width / 8); 109 [ # # ]: 0 : if (!info->iomem_addr) 110 : : return; 111 : : 112 [ # # ]: 0 : if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0)) 113 : : return; 114 : : 115 : : /* Silently fail, if cpuidle attribute group is not present */ 116 : 0 : sysfs_add_file_to_group(&cpu_subsys.dev_root->kobj, 117 : : &dev_attr_low_power_idle_system_residency_us.attr, 118 : : "cpuidle"); 119 [ # # ]: 0 : } else if (info->gaddr.space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) { 120 [ # # ]: 0 : if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0)) 121 : : return; 122 : : 123 : : /* Silently fail, if cpuidle attribute group is not present */ 124 : 0 : sysfs_add_file_to_group(&cpu_subsys.dev_root->kobj, 125 : : &dev_attr_low_power_idle_cpu_residency_us.attr, 126 : : "cpuidle"); 127 : : } 128 : : } 129 : : 130 : 0 : static void lpit_process(u64 begin, u64 end) 131 : : { 132 [ # # ]: 0 : while (begin + sizeof(struct acpi_lpit_native) <= end) { 133 : 0 : struct acpi_lpit_native *lpit_native = (struct acpi_lpit_native *)begin; 134 : : 135 [ # # # # ]: 0 : if (!lpit_native->header.type && !lpit_native->header.flags) { 136 [ # # ]: 0 : if (lpit_native->residency_counter.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY && 137 [ # # ]: 0 : !residency_info_mem.gaddr.address) { 138 : 0 : lpit_update_residency(&residency_info_mem, lpit_native); 139 [ # # ]: 0 : } else if (lpit_native->residency_counter.space_id == ACPI_ADR_SPACE_FIXED_HARDWARE && 140 [ # # ]: 0 : !residency_info_ffh.gaddr.address) { 141 : 0 : lpit_update_residency(&residency_info_ffh, lpit_native); 142 : : } 143 : : } 144 : 0 : begin += lpit_native->header.length; 145 : : } 146 : 0 : } 147 : : 148 : 78 : void acpi_init_lpit(void) 149 : : { 150 : 78 : acpi_status status; 151 : 78 : struct acpi_table_lpit *lpit; 152 : : 153 : 78 : status = acpi_get_table(ACPI_SIG_LPIT, 0, (struct acpi_table_header **)&lpit); 154 : : 155 [ + - ]: 78 : if (ACPI_FAILURE(status)) 156 : 78 : return; 157 : : 158 : 0 : lpit_process((u64)lpit + sizeof(*lpit), 159 : 0 : (u64)lpit + lpit->header.length); 160 : : }