acpi_lpit.c 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. /*
  2. * acpi_lpit.c - LPIT table processing functions
  3. *
  4. * Copyright (C) 2017 Intel Corporation. All rights reserved.
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License version
  8. * 2 as published by the Free Software Foundation.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. */
  15. #include <linux/cpu.h>
  16. #include <linux/acpi.h>
  17. #include <asm/msr.h>
  18. #include <asm/tsc.h>
  19. struct lpit_residency_info {
  20. struct acpi_generic_address gaddr;
  21. u64 frequency;
  22. void __iomem *iomem_addr;
  23. };
  24. /* Storage for an memory mapped and FFH based entries */
  25. static struct lpit_residency_info residency_info_mem;
  26. static struct lpit_residency_info residency_info_ffh;
  27. static int lpit_read_residency_counter_us(u64 *counter, bool io_mem)
  28. {
  29. int err;
  30. if (io_mem) {
  31. u64 count = 0;
  32. int error;
  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. *counter = div64_u64(count * 1000000ULL, residency_info_mem.frequency);
  38. return 0;
  39. }
  40. err = rdmsrl_safe(residency_info_ffh.gaddr.address, counter);
  41. if (!err) {
  42. u64 mask = GENMASK_ULL(residency_info_ffh.gaddr.bit_offset +
  43. residency_info_ffh.gaddr. bit_width - 1,
  44. residency_info_ffh.gaddr.bit_offset);
  45. *counter &= mask;
  46. *counter >>= residency_info_ffh.gaddr.bit_offset;
  47. *counter = div64_u64(*counter * 1000000ULL, residency_info_ffh.frequency);
  48. return 0;
  49. }
  50. return -ENODATA;
  51. }
  52. static ssize_t low_power_idle_system_residency_us_show(struct device *dev,
  53. struct device_attribute *attr,
  54. char *buf)
  55. {
  56. u64 counter;
  57. int ret;
  58. ret = lpit_read_residency_counter_us(&counter, true);
  59. if (ret)
  60. return ret;
  61. return sprintf(buf, "%llu\n", counter);
  62. }
  63. static DEVICE_ATTR_RO(low_power_idle_system_residency_us);
  64. static ssize_t low_power_idle_cpu_residency_us_show(struct device *dev,
  65. struct device_attribute *attr,
  66. char *buf)
  67. {
  68. u64 counter;
  69. int ret;
  70. ret = lpit_read_residency_counter_us(&counter, false);
  71. if (ret)
  72. return ret;
  73. return sprintf(buf, "%llu\n", counter);
  74. }
  75. static DEVICE_ATTR_RO(low_power_idle_cpu_residency_us);
  76. int lpit_read_residency_count_address(u64 *address)
  77. {
  78. if (!residency_info_mem.gaddr.address)
  79. return -EINVAL;
  80. *address = residency_info_mem.gaddr.address;
  81. return 0;
  82. }
  83. EXPORT_SYMBOL_GPL(lpit_read_residency_count_address);
  84. static void lpit_update_residency(struct lpit_residency_info *info,
  85. struct acpi_lpit_native *lpit_native)
  86. {
  87. info->frequency = lpit_native->counter_frequency ?
  88. lpit_native->counter_frequency : tsc_khz * 1000;
  89. if (!info->frequency)
  90. info->frequency = 1;
  91. info->gaddr = lpit_native->residency_counter;
  92. if (info->gaddr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
  93. info->iomem_addr = ioremap_nocache(info->gaddr.address,
  94. info->gaddr.bit_width / 8);
  95. if (!info->iomem_addr)
  96. return;
  97. if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0))
  98. return;
  99. /* Silently fail, if cpuidle attribute group is not present */
  100. sysfs_add_file_to_group(&cpu_subsys.dev_root->kobj,
  101. &dev_attr_low_power_idle_system_residency_us.attr,
  102. "cpuidle");
  103. } else if (info->gaddr.space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) {
  104. if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0))
  105. return;
  106. /* Silently fail, if cpuidle attribute group is not present */
  107. sysfs_add_file_to_group(&cpu_subsys.dev_root->kobj,
  108. &dev_attr_low_power_idle_cpu_residency_us.attr,
  109. "cpuidle");
  110. }
  111. }
  112. static void lpit_process(u64 begin, u64 end)
  113. {
  114. while (begin + sizeof(struct acpi_lpit_native) < end) {
  115. struct acpi_lpit_native *lpit_native = (struct acpi_lpit_native *)begin;
  116. if (!lpit_native->header.type && !lpit_native->header.flags) {
  117. if (lpit_native->residency_counter.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY &&
  118. !residency_info_mem.gaddr.address) {
  119. lpit_update_residency(&residency_info_mem, lpit_native);
  120. } else if (lpit_native->residency_counter.space_id == ACPI_ADR_SPACE_FIXED_HARDWARE &&
  121. !residency_info_ffh.gaddr.address) {
  122. lpit_update_residency(&residency_info_ffh, lpit_native);
  123. }
  124. }
  125. begin += lpit_native->header.length;
  126. }
  127. }
  128. void acpi_init_lpit(void)
  129. {
  130. acpi_status status;
  131. u64 lpit_begin;
  132. struct acpi_table_lpit *lpit;
  133. status = acpi_get_table(ACPI_SIG_LPIT, 0, (struct acpi_table_header **)&lpit);
  134. if (ACPI_FAILURE(status))
  135. return;
  136. lpit_begin = (u64)lpit + sizeof(*lpit);
  137. lpit_process(lpit_begin, lpit_begin + lpit->header.length);
  138. }