acpi_lpit.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * acpi_lpit.c - LPIT table processing functions
  4. *
  5. * Copyright (C) 2017 Intel Corporation. All rights reserved.
  6. */
  7. #include <linux/cpu.h>
  8. #include <linux/acpi.h>
  9. #include <asm/msr.h>
  10. #include <asm/tsc.h>
  11. #include "internal.h"
  12. struct lpit_residency_info {
  13. struct acpi_generic_address gaddr;
  14. u64 frequency;
  15. void __iomem *iomem_addr;
  16. };
  17. /* Storage for an memory mapped and FFH based entries */
  18. static struct lpit_residency_info residency_info_mem;
  19. static struct lpit_residency_info residency_info_ffh;
  20. static int lpit_read_residency_counter_us(u64 *counter, bool io_mem)
  21. {
  22. int err;
  23. if (io_mem) {
  24. u64 count = 0;
  25. int error;
  26. error = acpi_os_read_iomem(residency_info_mem.iomem_addr, &count,
  27. residency_info_mem.gaddr.bit_width);
  28. if (error)
  29. return error;
  30. *counter = div64_u64(count * 1000000ULL, residency_info_mem.frequency);
  31. return 0;
  32. }
  33. err = rdmsrl_safe(residency_info_ffh.gaddr.address, counter);
  34. if (!err) {
  35. u64 mask = GENMASK_ULL(residency_info_ffh.gaddr.bit_offset +
  36. residency_info_ffh.gaddr. bit_width - 1,
  37. residency_info_ffh.gaddr.bit_offset);
  38. *counter &= mask;
  39. *counter >>= residency_info_ffh.gaddr.bit_offset;
  40. *counter = div64_u64(*counter * 1000000ULL, residency_info_ffh.frequency);
  41. return 0;
  42. }
  43. return -ENODATA;
  44. }
  45. static ssize_t low_power_idle_system_residency_us_show(struct device *dev,
  46. struct device_attribute *attr,
  47. char *buf)
  48. {
  49. u64 counter;
  50. int ret;
  51. ret = lpit_read_residency_counter_us(&counter, true);
  52. if (ret)
  53. return ret;
  54. return sprintf(buf, "%llu\n", counter);
  55. }
  56. static DEVICE_ATTR_RO(low_power_idle_system_residency_us);
  57. static ssize_t low_power_idle_cpu_residency_us_show(struct device *dev,
  58. struct device_attribute *attr,
  59. char *buf)
  60. {
  61. u64 counter;
  62. int ret;
  63. ret = lpit_read_residency_counter_us(&counter, false);
  64. if (ret)
  65. return ret;
  66. return sprintf(buf, "%llu\n", counter);
  67. }
  68. static DEVICE_ATTR_RO(low_power_idle_cpu_residency_us);
  69. int lpit_read_residency_count_address(u64 *address)
  70. {
  71. if (!residency_info_mem.gaddr.address)
  72. return -EINVAL;
  73. *address = residency_info_mem.gaddr.address;
  74. return 0;
  75. }
  76. EXPORT_SYMBOL_GPL(lpit_read_residency_count_address);
  77. static void lpit_update_residency(struct lpit_residency_info *info,
  78. struct acpi_lpit_native *lpit_native)
  79. {
  80. struct device *dev_root = bus_get_dev_root(&cpu_subsys);
  81. /* Silently fail, if cpuidle attribute group is not present */
  82. if (!dev_root)
  83. return;
  84. info->frequency = lpit_native->counter_frequency ?
  85. lpit_native->counter_frequency : mul_u32_u32(tsc_khz, 1000U);
  86. if (!info->frequency)
  87. info->frequency = 1;
  88. info->gaddr = lpit_native->residency_counter;
  89. if (info->gaddr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
  90. info->iomem_addr = ioremap(info->gaddr.address,
  91. info->gaddr.bit_width / 8);
  92. if (!info->iomem_addr)
  93. goto exit;
  94. sysfs_add_file_to_group(&dev_root->kobj,
  95. &dev_attr_low_power_idle_system_residency_us.attr,
  96. "cpuidle");
  97. } else if (info->gaddr.space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) {
  98. sysfs_add_file_to_group(&dev_root->kobj,
  99. &dev_attr_low_power_idle_cpu_residency_us.attr,
  100. "cpuidle");
  101. }
  102. exit:
  103. put_device(dev_root);
  104. }
  105. static void lpit_process(u64 begin, u64 end)
  106. {
  107. while (begin + sizeof(struct acpi_lpit_native) <= end) {
  108. struct acpi_lpit_native *lpit_native = (struct acpi_lpit_native *)begin;
  109. if (!lpit_native->header.type && !lpit_native->header.flags) {
  110. if (lpit_native->residency_counter.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY &&
  111. !residency_info_mem.gaddr.address) {
  112. lpit_update_residency(&residency_info_mem, lpit_native);
  113. } else if (lpit_native->residency_counter.space_id == ACPI_ADR_SPACE_FIXED_HARDWARE &&
  114. !residency_info_ffh.gaddr.address) {
  115. lpit_update_residency(&residency_info_ffh, lpit_native);
  116. }
  117. }
  118. begin += lpit_native->header.length;
  119. }
  120. }
  121. void acpi_init_lpit(void)
  122. {
  123. acpi_status status;
  124. struct acpi_table_lpit *lpit;
  125. status = acpi_get_table(ACPI_SIG_LPIT, 0, (struct acpi_table_header **)&lpit);
  126. if (ACPI_FAILURE(status))
  127. return;
  128. lpit_process((u64)lpit + sizeof(*lpit),
  129. (u64)lpit + lpit->header.length);
  130. acpi_put_table((struct acpi_table_header *)lpit);
  131. }