diff options
-rw-r--r-- | Documentation/acpi/lpit.txt | 25 | ||||
-rw-r--r-- | drivers/acpi/Kconfig | 5 | ||||
-rw-r--r-- | drivers/acpi/Makefile | 1 | ||||
-rw-r--r-- | drivers/acpi/acpi_lpit.c | 162 | ||||
-rw-r--r-- | drivers/acpi/internal.h | 6 | ||||
-rw-r--r-- | drivers/acpi/osl.c | 42 | ||||
-rw-r--r-- | drivers/acpi/scan.c | 1 | ||||
-rw-r--r-- | include/acpi/acpiosxf.h | 2 | ||||
-rw-r--r-- | include/linux/acpi.h | 9 |
9 files changed, 237 insertions, 16 deletions
diff --git a/Documentation/acpi/lpit.txt b/Documentation/acpi/lpit.txt new file mode 100644 index 000000000000..b426398d2e97 --- /dev/null +++ b/Documentation/acpi/lpit.txt | |||
@@ -0,0 +1,25 @@ | |||
1 | To enumerate platform Low Power Idle states, Intel platforms are using | ||
2 | “Low Power Idle Table” (LPIT). More details about this table can be | ||
3 | downloaded from: | ||
4 | http://www.uefi.org/sites/default/files/resources/Intel_ACPI_Low_Power_S0_Idle.pdf | ||
5 | |||
6 | Residencies for each low power state can be read via FFH | ||
7 | (Function fixed hardware) or a memory mapped interface. | ||
8 | |||
9 | On platforms supporting S0ix sleep states, there can be two types of | ||
10 | residencies: | ||
11 | - CPU PKG C10 (Read via FFH interface) | ||
12 | - Platform Controller Hub (PCH) SLP_S0 (Read via memory mapped interface) | ||
13 | |||
14 | The following attributes are added dynamically to the cpuidle | ||
15 | sysfs attribute group: | ||
16 | /sys/devices/system/cpu/cpuidle/low_power_idle_cpu_residency_us | ||
17 | /sys/devices/system/cpu/cpuidle/low_power_idle_system_residency_us | ||
18 | |||
19 | The "low_power_idle_cpu_residency_us" attribute shows time spent | ||
20 | by the CPU package in PKG C10 | ||
21 | |||
22 | The "low_power_idle_system_residency_us" attribute shows SLP_S0 | ||
23 | residency, or system time spent with the SLP_S0# signal asserted. | ||
24 | This is the lowest possible system power state, achieved only when CPU is in | ||
25 | PKG C10 and all functional blocks in PCH are in a low power state. | ||
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 1ce52f84dc23..4bfef0f78cde 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig | |||
@@ -80,6 +80,11 @@ endif | |||
80 | config ACPI_SPCR_TABLE | 80 | config ACPI_SPCR_TABLE |
81 | bool | 81 | bool |
82 | 82 | ||
83 | config ACPI_LPIT | ||
84 | bool | ||
85 | depends on X86_64 | ||
86 | default y | ||
87 | |||
83 | config ACPI_SLEEP | 88 | config ACPI_SLEEP |
84 | bool | 89 | bool |
85 | depends on SUSPEND || HIBERNATION | 90 | depends on SUSPEND || HIBERNATION |
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 90265ab4437a..6a19bd7aba21 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile | |||
@@ -56,6 +56,7 @@ acpi-$(CONFIG_DEBUG_FS) += debugfs.o | |||
56 | acpi-$(CONFIG_ACPI_NUMA) += numa.o | 56 | acpi-$(CONFIG_ACPI_NUMA) += numa.o |
57 | acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o | 57 | acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o |
58 | acpi-y += acpi_lpat.o | 58 | acpi-y += acpi_lpat.o |
59 | acpi-$(CONFIG_ACPI_LPIT) += acpi_lpit.o | ||
59 | acpi-$(CONFIG_ACPI_GENERIC_GSI) += irq.o | 60 | acpi-$(CONFIG_ACPI_GENERIC_GSI) += irq.o |
60 | acpi-$(CONFIG_ACPI_WATCHDOG) += acpi_watchdog.o | 61 | acpi-$(CONFIG_ACPI_WATCHDOG) += acpi_watchdog.o |
61 | 62 | ||
diff --git a/drivers/acpi/acpi_lpit.c b/drivers/acpi/acpi_lpit.c new file mode 100644 index 000000000000..e94e478dd18b --- /dev/null +++ b/drivers/acpi/acpi_lpit.c | |||
@@ -0,0 +1,162 @@ | |||
1 | |||
2 | /* | ||
3 | * acpi_lpit.c - LPIT table processing functions | ||
4 | * | ||
5 | * Copyright (C) 2017 Intel Corporation. All rights reserved. | ||
6 | * | ||
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. | ||
10 | * | ||
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. | ||
15 | */ | ||
16 | |||
17 | #include <linux/cpu.h> | ||
18 | #include <linux/acpi.h> | ||
19 | #include <asm/msr.h> | ||
20 | #include <asm/tsc.h> | ||
21 | |||
22 | struct lpit_residency_info { | ||
23 | struct acpi_generic_address gaddr; | ||
24 | u64 frequency; | ||
25 | void __iomem *iomem_addr; | ||
26 | }; | ||
27 | |||
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; | ||
31 | |||
32 | static int lpit_read_residency_counter_us(u64 *counter, bool io_mem) | ||
33 | { | ||
34 | int err; | ||
35 | |||
36 | if (io_mem) { | ||
37 | u64 count = 0; | ||
38 | int error; | ||
39 | |||
40 | error = acpi_os_read_iomem(residency_info_mem.iomem_addr, &count, | ||
41 | residency_info_mem.gaddr.bit_width); | ||
42 | if (error) | ||
43 | return error; | ||
44 | |||
45 | *counter = div64_u64(count * 1000000ULL, residency_info_mem.frequency); | ||
46 | return 0; | ||
47 | } | ||
48 | |||
49 | err = rdmsrl_safe(residency_info_ffh.gaddr.address, counter); | ||
50 | if (!err) { | ||
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); | ||
54 | |||
55 | *counter &= mask; | ||
56 | *counter >>= residency_info_ffh.gaddr.bit_offset; | ||
57 | *counter = div64_u64(*counter * 1000000ULL, residency_info_ffh.frequency); | ||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | return -ENODATA; | ||
62 | } | ||
63 | |||
64 | static ssize_t low_power_idle_system_residency_us_show(struct device *dev, | ||
65 | struct device_attribute *attr, | ||
66 | char *buf) | ||
67 | { | ||
68 | u64 counter; | ||
69 | int ret; | ||
70 | |||
71 | ret = lpit_read_residency_counter_us(&counter, true); | ||
72 | if (ret) | ||
73 | return ret; | ||
74 | |||
75 | return sprintf(buf, "%llu\n", counter); | ||
76 | } | ||
77 | static DEVICE_ATTR_RO(low_power_idle_system_residency_us); | ||
78 | |||
79 | static ssize_t low_power_idle_cpu_residency_us_show(struct device *dev, | ||
80 | struct device_attribute *attr, | ||
81 | char *buf) | ||
82 | { | ||
83 | u64 counter; | ||
84 | int ret; | ||
85 | |||
86 | ret = lpit_read_residency_counter_us(&counter, false); | ||
87 | if (ret) | ||
88 | return ret; | ||
89 | |||
90 | return sprintf(buf, "%llu\n", counter); | ||
91 | } | ||
92 | static DEVICE_ATTR_RO(low_power_idle_cpu_residency_us); | ||
93 | |||
94 | int lpit_read_residency_count_address(u64 *address) | ||
95 | { | ||
96 | if (!residency_info_mem.gaddr.address) | ||
97 | return -EINVAL; | ||
98 | |||
99 | *address = residency_info_mem.gaddr.address; | ||
100 | |||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static void lpit_update_residency(struct lpit_residency_info *info, | ||
105 | struct acpi_lpit_native *lpit_native) | ||
106 | { | ||
107 | info->frequency = lpit_native->counter_frequency ? | ||
108 | lpit_native->counter_frequency : tsc_khz * 1000; | ||
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_nocache(info->gaddr.address, | ||
115 | info->gaddr.bit_width / 8); | ||
116 | if (!info->iomem_addr) | ||
117 | return; | ||
118 | |||
119 | /* Silently fail, if cpuidle attribute group is not present */ | ||
120 | sysfs_add_file_to_group(&cpu_subsys.dev_root->kobj, | ||
121 | &dev_attr_low_power_idle_system_residency_us.attr, | ||
122 | "cpuidle"); | ||
123 | } else if (info->gaddr.space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) { | ||
124 | /* Silently fail, if cpuidle attribute group is not present */ | ||
125 | sysfs_add_file_to_group(&cpu_subsys.dev_root->kobj, | ||
126 | &dev_attr_low_power_idle_cpu_residency_us.attr, | ||
127 | "cpuidle"); | ||
128 | } | ||
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 | u64 lpit_begin; | ||
153 | struct acpi_table_lpit *lpit; | ||
154 | |||
155 | status = acpi_get_table(ACPI_SIG_LPIT, 0, (struct acpi_table_header **)&lpit); | ||
156 | |||
157 | if (ACPI_FAILURE(status)) | ||
158 | return; | ||
159 | |||
160 | lpit_begin = (u64)lpit + sizeof(*lpit); | ||
161 | lpit_process(lpit_begin, lpit_begin + lpit->header.length); | ||
162 | } | ||
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 4361c4415b4f..fc8c43e76707 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h | |||
@@ -248,4 +248,10 @@ void acpi_watchdog_init(void); | |||
248 | static inline void acpi_watchdog_init(void) {} | 248 | static inline void acpi_watchdog_init(void) {} |
249 | #endif | 249 | #endif |
250 | 250 | ||
251 | #ifdef CONFIG_ACPI_LPIT | ||
252 | void acpi_init_lpit(void); | ||
253 | #else | ||
254 | static inline void acpi_init_lpit(void) { } | ||
255 | #endif | ||
256 | |||
251 | #endif /* _ACPI_INTERNAL_H_ */ | 257 | #endif /* _ACPI_INTERNAL_H_ */ |
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index db78d353bab1..3bb46cb24a99 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c | |||
@@ -663,6 +663,29 @@ acpi_status acpi_os_write_port(acpi_io_address port, u32 value, u32 width) | |||
663 | 663 | ||
664 | EXPORT_SYMBOL(acpi_os_write_port); | 664 | EXPORT_SYMBOL(acpi_os_write_port); |
665 | 665 | ||
666 | int acpi_os_read_iomem(void __iomem *virt_addr, u64 *value, u32 width) | ||
667 | { | ||
668 | |||
669 | switch (width) { | ||
670 | case 8: | ||
671 | *(u8 *) value = readb(virt_addr); | ||
672 | break; | ||
673 | case 16: | ||
674 | *(u16 *) value = readw(virt_addr); | ||
675 | break; | ||
676 | case 32: | ||
677 | *(u32 *) value = readl(virt_addr); | ||
678 | break; | ||
679 | case 64: | ||
680 | *(u64 *) value = readq(virt_addr); | ||
681 | break; | ||
682 | default: | ||
683 | return -EINVAL; | ||
684 | } | ||
685 | |||
686 | return 0; | ||
687 | } | ||
688 | |||
666 | acpi_status | 689 | acpi_status |
667 | acpi_os_read_memory(acpi_physical_address phys_addr, u64 *value, u32 width) | 690 | acpi_os_read_memory(acpi_physical_address phys_addr, u64 *value, u32 width) |
668 | { | 691 | { |
@@ -670,6 +693,7 @@ acpi_os_read_memory(acpi_physical_address phys_addr, u64 *value, u32 width) | |||
670 | unsigned int size = width / 8; | 693 | unsigned int size = width / 8; |
671 | bool unmap = false; | 694 | bool unmap = false; |
672 | u64 dummy; | 695 | u64 dummy; |
696 | int error; | ||
673 | 697 | ||
674 | rcu_read_lock(); | 698 | rcu_read_lock(); |
675 | virt_addr = acpi_map_vaddr_lookup(phys_addr, size); | 699 | virt_addr = acpi_map_vaddr_lookup(phys_addr, size); |
@@ -684,22 +708,8 @@ acpi_os_read_memory(acpi_physical_address phys_addr, u64 *value, u32 width) | |||
684 | if (!value) | 708 | if (!value) |
685 | value = &dummy; | 709 | value = &dummy; |
686 | 710 | ||
687 | switch (width) { | 711 | error = acpi_os_read_iomem(virt_addr, value, width); |
688 | case 8: | 712 | BUG_ON(error); |
689 | *(u8 *) value = readb(virt_addr); | ||
690 | break; | ||
691 | case 16: | ||
692 | *(u16 *) value = readw(virt_addr); | ||
693 | break; | ||
694 | case 32: | ||
695 | *(u32 *) value = readl(virt_addr); | ||
696 | break; | ||
697 | case 64: | ||
698 | *(u64 *) value = readq(virt_addr); | ||
699 | break; | ||
700 | default: | ||
701 | BUG(); | ||
702 | } | ||
703 | 713 | ||
704 | if (unmap) | 714 | if (unmap) |
705 | iounmap(virt_addr); | 715 | iounmap(virt_addr); |
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 602f8ff212f2..81367edc8a10 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c | |||
@@ -2122,6 +2122,7 @@ int __init acpi_scan_init(void) | |||
2122 | acpi_int340x_thermal_init(); | 2122 | acpi_int340x_thermal_init(); |
2123 | acpi_amba_init(); | 2123 | acpi_amba_init(); |
2124 | acpi_watchdog_init(); | 2124 | acpi_watchdog_init(); |
2125 | acpi_init_lpit(); | ||
2125 | 2126 | ||
2126 | acpi_scan_add_handler(&generic_device_handler); | 2127 | acpi_scan_add_handler(&generic_device_handler); |
2127 | 2128 | ||
diff --git a/include/acpi/acpiosxf.h b/include/acpi/acpiosxf.h index c66eb8ffa454..d5c0f5153c4e 100644 --- a/include/acpi/acpiosxf.h +++ b/include/acpi/acpiosxf.h | |||
@@ -287,6 +287,8 @@ acpi_status acpi_os_write_port(acpi_io_address address, u32 value, u32 width); | |||
287 | /* | 287 | /* |
288 | * Platform and hardware-independent physical memory interfaces | 288 | * Platform and hardware-independent physical memory interfaces |
289 | */ | 289 | */ |
290 | int acpi_os_read_iomem(void __iomem *virt_addr, u64 *value, u32 width); | ||
291 | |||
290 | #ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_read_memory | 292 | #ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_read_memory |
291 | acpi_status | 293 | acpi_status |
292 | acpi_os_read_memory(acpi_physical_address address, u64 *value, u32 width); | 294 | acpi_os_read_memory(acpi_physical_address address, u64 *value, u32 width); |
diff --git a/include/linux/acpi.h b/include/linux/acpi.h index d18c92d4ba19..2b1738f840ab 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h | |||
@@ -1248,4 +1248,13 @@ int acpi_irq_get(acpi_handle handle, unsigned int index, struct resource *res) | |||
1248 | } | 1248 | } |
1249 | #endif | 1249 | #endif |
1250 | 1250 | ||
1251 | #ifdef CONFIG_ACPI_LPIT | ||
1252 | int lpit_read_residency_count_address(u64 *address); | ||
1253 | #else | ||
1254 | static inline int lpit_read_residency_count_address(u64 *address) | ||
1255 | { | ||
1256 | return -EINVAL; | ||
1257 | } | ||
1258 | #endif | ||
1259 | |||
1251 | #endif /*_LINUX_ACPI_H*/ | 1260 | #endif /*_LINUX_ACPI_H*/ |