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 struct device *dev_root = bus_get_dev_root(&cpu_subsys); 102 103 /* Silently fail, if cpuidle attribute group is not present */ 104 if (!dev_root) 105 return; 106 107 info->frequency = lpit_native->counter_frequency ? 108 lpit_native->counter_frequency : mul_u32_u32(tsc_khz, 1000U); 109 if (!info->frequency) 110 info->frequency = 1; 111 112 info->gaddr = lpit_native->residency_counter; 113 if (info->gaddr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { 114 info->iomem_addr = ioremap(info->gaddr.address, 115 info->gaddr.bit_width / 8); 116 if (!info->iomem_addr) 117 goto exit; 118 119 sysfs_add_file_to_group(&dev_root->kobj, 120 &dev_attr_low_power_idle_system_residency_us.attr, 121 "cpuidle"); 122 } else if (info->gaddr.space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) { 123 sysfs_add_file_to_group(&dev_root->kobj, 124 &dev_attr_low_power_idle_cpu_residency_us.attr, 125 "cpuidle"); 126 } 127 exit: 128 put_device(dev_root); 129 } 130 131 static void lpit_process(u64 begin, u64 end) 132 { 133 while (begin + sizeof(struct acpi_lpit_native) <= end) { 134 struct acpi_lpit_native *lpit_native = (struct acpi_lpit_native *)begin; 135 136 if (!lpit_native->header.type && !lpit_native->header.flags) { 137 if (lpit_native->residency_counter.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY && 138 !residency_info_mem.gaddr.address) { 139 lpit_update_residency(&residency_info_mem, lpit_native); 140 } else if (lpit_native->residency_counter.space_id == ACPI_ADR_SPACE_FIXED_HARDWARE && 141 !residency_info_ffh.gaddr.address) { 142 lpit_update_residency(&residency_info_ffh, lpit_native); 143 } 144 } 145 begin += lpit_native->header.length; 146 } 147 } 148 149 void acpi_init_lpit(void) 150 { 151 acpi_status status; 152 struct acpi_table_lpit *lpit; 153 154 status = acpi_get_table(ACPI_SIG_LPIT, 0, (struct acpi_table_header **)&lpit); 155 if (ACPI_FAILURE(status)) 156 return; 157 158 lpit_process((u64)lpit + sizeof(*lpit), 159 (u64)lpit + lpit->header.length); 160 161 acpi_put_table((struct acpi_table_header *)lpit); 162 } 163