1 // SPDX-License-Identifier: GPL-2.0-only
4 * acpi_lpit.c - LPIT table processing functions
6 * Copyright (C) 2017 Intel Corporation. All rights reserved.
10 #include <linux/acpi.h>
14 struct lpit_residency_info
{
15 struct acpi_generic_address gaddr
;
17 void __iomem
*iomem_addr
;
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
;
24 static int lpit_read_residency_counter_us(u64
*counter
, bool io_mem
)
32 error
= acpi_os_read_iomem(residency_info_mem
.iomem_addr
, &count
,
33 residency_info_mem
.gaddr
.bit_width
);
37 *counter
= div64_u64(count
* 1000000ULL, residency_info_mem
.frequency
);
41 err
= rdmsrl_safe(residency_info_ffh
.gaddr
.address
, counter
);
43 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
);
48 *counter
>>= residency_info_ffh
.gaddr
.bit_offset
;
49 *counter
= div64_u64(*counter
* 1000000ULL, residency_info_ffh
.frequency
);
56 static ssize_t
low_power_idle_system_residency_us_show(struct device
*dev
,
57 struct device_attribute
*attr
,
63 ret
= lpit_read_residency_counter_us(&counter
, true);
67 return sprintf(buf
, "%llu\n", counter
);
69 static DEVICE_ATTR_RO(low_power_idle_system_residency_us
);
71 static ssize_t
low_power_idle_cpu_residency_us_show(struct device
*dev
,
72 struct device_attribute
*attr
,
78 ret
= lpit_read_residency_counter_us(&counter
, false);
82 return sprintf(buf
, "%llu\n", counter
);
84 static DEVICE_ATTR_RO(low_power_idle_cpu_residency_us
);
86 int lpit_read_residency_count_address(u64
*address
)
88 if (!residency_info_mem
.gaddr
.address
)
91 *address
= residency_info_mem
.gaddr
.address
;
95 EXPORT_SYMBOL_GPL(lpit_read_residency_count_address
);
97 static void lpit_update_residency(struct lpit_residency_info
*info
,
98 struct acpi_lpit_native
*lpit_native
)
100 info
->frequency
= lpit_native
->counter_frequency
?
101 lpit_native
->counter_frequency
: tsc_khz
* 1000;
102 if (!info
->frequency
)
105 info
->gaddr
= lpit_native
->residency_counter
;
106 if (info
->gaddr
.space_id
== ACPI_ADR_SPACE_SYSTEM_MEMORY
) {
107 info
->iomem_addr
= ioremap_nocache(info
->gaddr
.address
,
108 info
->gaddr
.bit_width
/ 8);
109 if (!info
->iomem_addr
)
112 if (!(acpi_gbl_FADT
.flags
& ACPI_FADT_LOW_POWER_S0
))
115 /* Silently fail, if cpuidle attribute group is not present */
116 sysfs_add_file_to_group(&cpu_subsys
.dev_root
->kobj
,
117 &dev_attr_low_power_idle_system_residency_us
.attr
,
119 } else if (info
->gaddr
.space_id
== ACPI_ADR_SPACE_FIXED_HARDWARE
) {
120 if (!(acpi_gbl_FADT
.flags
& ACPI_FADT_LOW_POWER_S0
))
123 /* Silently fail, if cpuidle attribute group is not present */
124 sysfs_add_file_to_group(&cpu_subsys
.dev_root
->kobj
,
125 &dev_attr_low_power_idle_cpu_residency_us
.attr
,
130 static void lpit_process(u64 begin
, u64 end
)
132 while (begin
+ sizeof(struct acpi_lpit_native
) <= end
) {
133 struct acpi_lpit_native
*lpit_native
= (struct acpi_lpit_native
*)begin
;
135 if (!lpit_native
->header
.type
&& !lpit_native
->header
.flags
) {
136 if (lpit_native
->residency_counter
.space_id
== ACPI_ADR_SPACE_SYSTEM_MEMORY
&&
137 !residency_info_mem
.gaddr
.address
) {
138 lpit_update_residency(&residency_info_mem
, lpit_native
);
139 } else if (lpit_native
->residency_counter
.space_id
== ACPI_ADR_SPACE_FIXED_HARDWARE
&&
140 !residency_info_ffh
.gaddr
.address
) {
141 lpit_update_residency(&residency_info_ffh
, lpit_native
);
144 begin
+= lpit_native
->header
.length
;
148 void acpi_init_lpit(void)
151 struct acpi_table_lpit
*lpit
;
153 status
= acpi_get_table(ACPI_SIG_LPIT
, 0, (struct acpi_table_header
**)&lpit
);
155 if (ACPI_FAILURE(status
))
158 lpit_process((u64
)lpit
+ sizeof(*lpit
),
159 (u64
)lpit
+ lpit
->header
.length
);