18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci/* 48c2ecf20Sopenharmony_ci * acpi_lpit.c - LPIT table processing functions 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2017 Intel Corporation. All rights reserved. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/cpu.h> 108c2ecf20Sopenharmony_ci#include <linux/acpi.h> 118c2ecf20Sopenharmony_ci#include <asm/msr.h> 128c2ecf20Sopenharmony_ci#include <asm/tsc.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistruct lpit_residency_info { 158c2ecf20Sopenharmony_ci struct acpi_generic_address gaddr; 168c2ecf20Sopenharmony_ci u64 frequency; 178c2ecf20Sopenharmony_ci void __iomem *iomem_addr; 188c2ecf20Sopenharmony_ci}; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* Storage for an memory mapped and FFH based entries */ 218c2ecf20Sopenharmony_cistatic struct lpit_residency_info residency_info_mem; 228c2ecf20Sopenharmony_cistatic struct lpit_residency_info residency_info_ffh; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic int lpit_read_residency_counter_us(u64 *counter, bool io_mem) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci int err; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci if (io_mem) { 298c2ecf20Sopenharmony_ci u64 count = 0; 308c2ecf20Sopenharmony_ci int error; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci error = acpi_os_read_iomem(residency_info_mem.iomem_addr, &count, 338c2ecf20Sopenharmony_ci residency_info_mem.gaddr.bit_width); 348c2ecf20Sopenharmony_ci if (error) 358c2ecf20Sopenharmony_ci return error; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci *counter = div64_u64(count * 1000000ULL, residency_info_mem.frequency); 388c2ecf20Sopenharmony_ci return 0; 398c2ecf20Sopenharmony_ci } 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci err = rdmsrl_safe(residency_info_ffh.gaddr.address, counter); 428c2ecf20Sopenharmony_ci if (!err) { 438c2ecf20Sopenharmony_ci u64 mask = GENMASK_ULL(residency_info_ffh.gaddr.bit_offset + 448c2ecf20Sopenharmony_ci residency_info_ffh.gaddr. bit_width - 1, 458c2ecf20Sopenharmony_ci residency_info_ffh.gaddr.bit_offset); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci *counter &= mask; 488c2ecf20Sopenharmony_ci *counter >>= residency_info_ffh.gaddr.bit_offset; 498c2ecf20Sopenharmony_ci *counter = div64_u64(*counter * 1000000ULL, residency_info_ffh.frequency); 508c2ecf20Sopenharmony_ci return 0; 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci return -ENODATA; 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic ssize_t low_power_idle_system_residency_us_show(struct device *dev, 578c2ecf20Sopenharmony_ci struct device_attribute *attr, 588c2ecf20Sopenharmony_ci char *buf) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci u64 counter; 618c2ecf20Sopenharmony_ci int ret; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci ret = lpit_read_residency_counter_us(&counter, true); 648c2ecf20Sopenharmony_ci if (ret) 658c2ecf20Sopenharmony_ci return ret; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci return sprintf(buf, "%llu\n", counter); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(low_power_idle_system_residency_us); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic ssize_t low_power_idle_cpu_residency_us_show(struct device *dev, 728c2ecf20Sopenharmony_ci struct device_attribute *attr, 738c2ecf20Sopenharmony_ci char *buf) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci u64 counter; 768c2ecf20Sopenharmony_ci int ret; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci ret = lpit_read_residency_counter_us(&counter, false); 798c2ecf20Sopenharmony_ci if (ret) 808c2ecf20Sopenharmony_ci return ret; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci return sprintf(buf, "%llu\n", counter); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(low_power_idle_cpu_residency_us); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ciint lpit_read_residency_count_address(u64 *address) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci if (!residency_info_mem.gaddr.address) 898c2ecf20Sopenharmony_ci return -EINVAL; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci *address = residency_info_mem.gaddr.address; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci return 0; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lpit_read_residency_count_address); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic void lpit_update_residency(struct lpit_residency_info *info, 988c2ecf20Sopenharmony_ci struct acpi_lpit_native *lpit_native) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci info->frequency = lpit_native->counter_frequency ? 1018c2ecf20Sopenharmony_ci lpit_native->counter_frequency : mul_u32_u32(tsc_khz, 1000U); 1028c2ecf20Sopenharmony_ci if (!info->frequency) 1038c2ecf20Sopenharmony_ci info->frequency = 1; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci info->gaddr = lpit_native->residency_counter; 1068c2ecf20Sopenharmony_ci if (info->gaddr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { 1078c2ecf20Sopenharmony_ci info->iomem_addr = ioremap(info->gaddr.address, 1088c2ecf20Sopenharmony_ci info->gaddr.bit_width / 8); 1098c2ecf20Sopenharmony_ci if (!info->iomem_addr) 1108c2ecf20Sopenharmony_ci return; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0)) 1138c2ecf20Sopenharmony_ci return; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* Silently fail, if cpuidle attribute group is not present */ 1168c2ecf20Sopenharmony_ci sysfs_add_file_to_group(&cpu_subsys.dev_root->kobj, 1178c2ecf20Sopenharmony_ci &dev_attr_low_power_idle_system_residency_us.attr, 1188c2ecf20Sopenharmony_ci "cpuidle"); 1198c2ecf20Sopenharmony_ci } else if (info->gaddr.space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) { 1208c2ecf20Sopenharmony_ci if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0)) 1218c2ecf20Sopenharmony_ci return; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* Silently fail, if cpuidle attribute group is not present */ 1248c2ecf20Sopenharmony_ci sysfs_add_file_to_group(&cpu_subsys.dev_root->kobj, 1258c2ecf20Sopenharmony_ci &dev_attr_low_power_idle_cpu_residency_us.attr, 1268c2ecf20Sopenharmony_ci "cpuidle"); 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic void lpit_process(u64 begin, u64 end) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci while (begin + sizeof(struct acpi_lpit_native) <= end) { 1338c2ecf20Sopenharmony_ci struct acpi_lpit_native *lpit_native = (struct acpi_lpit_native *)begin; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (!lpit_native->header.type && !lpit_native->header.flags) { 1368c2ecf20Sopenharmony_ci if (lpit_native->residency_counter.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY && 1378c2ecf20Sopenharmony_ci !residency_info_mem.gaddr.address) { 1388c2ecf20Sopenharmony_ci lpit_update_residency(&residency_info_mem, lpit_native); 1398c2ecf20Sopenharmony_ci } else if (lpit_native->residency_counter.space_id == ACPI_ADR_SPACE_FIXED_HARDWARE && 1408c2ecf20Sopenharmony_ci !residency_info_ffh.gaddr.address) { 1418c2ecf20Sopenharmony_ci lpit_update_residency(&residency_info_ffh, lpit_native); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci begin += lpit_native->header.length; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_civoid acpi_init_lpit(void) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci acpi_status status; 1518c2ecf20Sopenharmony_ci struct acpi_table_lpit *lpit; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci status = acpi_get_table(ACPI_SIG_LPIT, 0, (struct acpi_table_header **)&lpit); 1548c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 1558c2ecf20Sopenharmony_ci return; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci lpit_process((u64)lpit + sizeof(*lpit), 1588c2ecf20Sopenharmony_ci (u64)lpit + lpit->header.length); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci acpi_put_table((struct acpi_table_header *)lpit); 1618c2ecf20Sopenharmony_ci} 162