3 * acpi_lpit.c - LPIT table processing functions
5 * Copyright (C) 2017 Intel Corporation. All rights reserved.
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License version
9 * 2 as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
17 #include <linux/cpu.h>
18 #include <linux/acpi.h>
22 struct lpit_residency_info
{
23 struct acpi_generic_address gaddr
;
25 void __iomem
*iomem_addr
;
28 /* Storage for an memory mapped and FFH based entries */
29 static struct lpit_residency_info residency_info_mem
;
30 static struct lpit_residency_info residency_info_ffh
;
32 static int lpit_read_residency_counter_us(u64
*counter
, bool io_mem
)
40 error
= acpi_os_read_iomem(residency_info_mem
.iomem_addr
, &count
,
41 residency_info_mem
.gaddr
.bit_width
);
45 *counter
= div64_u64(count
* 1000000ULL, residency_info_mem
.frequency
);
49 err
= rdmsrl_safe(residency_info_ffh
.gaddr
.address
, counter
);
51 u64 mask
= GENMASK_ULL(residency_info_ffh
.gaddr
.bit_offset
+
52 residency_info_ffh
.gaddr
. bit_width
- 1,
53 residency_info_ffh
.gaddr
.bit_offset
);
56 *counter
>>= residency_info_ffh
.gaddr
.bit_offset
;
57 *counter
= div64_u64(*counter
* 1000000ULL, residency_info_ffh
.frequency
);
64 static ssize_t
low_power_idle_system_residency_us_show(struct device
*dev
,
65 struct device_attribute
*attr
,
71 ret
= lpit_read_residency_counter_us(&counter
, true);
75 return sprintf(buf
, "%llu\n", counter
);
77 static DEVICE_ATTR_RO(low_power_idle_system_residency_us
);
79 static ssize_t
low_power_idle_cpu_residency_us_show(struct device
*dev
,
80 struct device_attribute
*attr
,
86 ret
= lpit_read_residency_counter_us(&counter
, false);
90 return sprintf(buf
, "%llu\n", counter
);
92 static DEVICE_ATTR_RO(low_power_idle_cpu_residency_us
);
94 int lpit_read_residency_count_address(u64
*address
)
96 if (!residency_info_mem
.gaddr
.address
)
99 *address
= residency_info_mem
.gaddr
.address
;
103 EXPORT_SYMBOL_GPL(lpit_read_residency_count_address
);
105 static void lpit_update_residency(struct lpit_residency_info
*info
,
106 struct acpi_lpit_native
*lpit_native
)
108 info
->frequency
= lpit_native
->counter_frequency
?
109 lpit_native
->counter_frequency
: tsc_khz
* 1000;
110 if (!info
->frequency
)
113 info
->gaddr
= lpit_native
->residency_counter
;
114 if (info
->gaddr
.space_id
== ACPI_ADR_SPACE_SYSTEM_MEMORY
) {
115 info
->iomem_addr
= ioremap_nocache(info
->gaddr
.address
,
116 info
->gaddr
.bit_width
/ 8);
117 if (!info
->iomem_addr
)
120 /* Silently fail, if cpuidle attribute group is not present */
121 sysfs_add_file_to_group(&cpu_subsys
.dev_root
->kobj
,
122 &dev_attr_low_power_idle_system_residency_us
.attr
,
124 } else if (info
->gaddr
.space_id
== ACPI_ADR_SPACE_FIXED_HARDWARE
) {
125 /* Silently fail, if cpuidle attribute group is not present */
126 sysfs_add_file_to_group(&cpu_subsys
.dev_root
->kobj
,
127 &dev_attr_low_power_idle_cpu_residency_us
.attr
,
132 static void lpit_process(u64 begin
, u64 end
)
134 while (begin
+ sizeof(struct acpi_lpit_native
) < end
) {
135 struct acpi_lpit_native
*lpit_native
= (struct acpi_lpit_native
*)begin
;
137 if (!lpit_native
->header
.type
&& !lpit_native
->header
.flags
) {
138 if (lpit_native
->residency_counter
.space_id
== ACPI_ADR_SPACE_SYSTEM_MEMORY
&&
139 !residency_info_mem
.gaddr
.address
) {
140 lpit_update_residency(&residency_info_mem
, lpit_native
);
141 } else if (lpit_native
->residency_counter
.space_id
== ACPI_ADR_SPACE_FIXED_HARDWARE
&&
142 !residency_info_ffh
.gaddr
.address
) {
143 lpit_update_residency(&residency_info_ffh
, lpit_native
);
146 begin
+= lpit_native
->header
.length
;
150 void acpi_init_lpit(void)
154 struct acpi_table_lpit
*lpit
;
156 status
= acpi_get_table(ACPI_SIG_LPIT
, 0, (struct acpi_table_header
**)&lpit
);
158 if (ACPI_FAILURE(status
))
161 lpit_begin
= (u64
)lpit
+ sizeof(*lpit
);
162 lpit_process(lpit_begin
, lpit_begin
+ lpit
->header
.length
);