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>
15 struct lpit_residency_info
{
16 struct acpi_generic_address gaddr
;
18 void __iomem
*iomem_addr
;
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
;
25 static int lpit_read_residency_counter_us(u64
*counter
, bool io_mem
)
33 error
= acpi_os_read_iomem(residency_info_mem
.iomem_addr
, &count
,
34 residency_info_mem
.gaddr
.bit_width
);
38 *counter
= div64_u64(count
* 1000000ULL, residency_info_mem
.frequency
);
42 err
= rdmsrl_safe(residency_info_ffh
.gaddr
.address
, counter
);
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
);
49 *counter
>>= residency_info_ffh
.gaddr
.bit_offset
;
50 *counter
= div64_u64(*counter
* 1000000ULL, residency_info_ffh
.frequency
);
57 static ssize_t
low_power_idle_system_residency_us_show(struct device
*dev
,
58 struct device_attribute
*attr
,
64 ret
= lpit_read_residency_counter_us(&counter
, true);
68 return sprintf(buf
, "%llu\n", counter
);
70 static DEVICE_ATTR_RO(low_power_idle_system_residency_us
);
72 static ssize_t
low_power_idle_cpu_residency_us_show(struct device
*dev
,
73 struct device_attribute
*attr
,
79 ret
= lpit_read_residency_counter_us(&counter
, false);
83 return sprintf(buf
, "%llu\n", counter
);
85 static DEVICE_ATTR_RO(low_power_idle_cpu_residency_us
);
87 int lpit_read_residency_count_address(u64
*address
)
89 if (!residency_info_mem
.gaddr
.address
)
92 *address
= residency_info_mem
.gaddr
.address
;
96 EXPORT_SYMBOL_GPL(lpit_read_residency_count_address
);
98 static void lpit_update_residency(struct lpit_residency_info
*info
,
99 struct acpi_lpit_native
*lpit_native
)
101 struct device
*dev_root
= bus_get_dev_root(&cpu_subsys
);
103 /* Silently fail, if cpuidle attribute group is not present */
107 info
->frequency
= lpit_native
->counter_frequency
?
108 lpit_native
->counter_frequency
: mul_u32_u32(tsc_khz
, 1000U);
109 if (!info
->frequency
)
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
)
119 sysfs_add_file_to_group(&dev_root
->kobj
,
120 &dev_attr_low_power_idle_system_residency_us
.attr
,
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
,
128 put_device(dev_root
);
131 static void lpit_process(u64 begin
, u64 end
)
133 while (begin
+ sizeof(struct acpi_lpit_native
) <= end
) {
134 struct acpi_lpit_native
*lpit_native
= (struct acpi_lpit_native
*)begin
;
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
);
145 begin
+= lpit_native
->header
.length
;
149 void acpi_init_lpit(void)
152 struct acpi_table_lpit
*lpit
;
154 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
);
161 acpi_put_table((struct acpi_table_header
*)lpit
);