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