intel_pmc_core.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. /*
  2. * Intel Core SoC Power Management Controller Driver
  3. *
  4. * Copyright (c) 2016, Intel Corporation.
  5. * All Rights Reserved.
  6. *
  7. * Authors: Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com>
  8. * Vishwanath Somayaji <vishwanath.somayaji@intel.com>
  9. *
  10. * This program is free software; you can redistribute it and/or modify it
  11. * under the terms and conditions of the GNU General Public License,
  12. * version 2, as published by the Free Software Foundation.
  13. *
  14. * This program is distributed in the hope it will be useful, but WITHOUT
  15. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  16. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  17. * more details.
  18. *
  19. */
  20. #include <linux/debugfs.h>
  21. #include <linux/device.h>
  22. #include <linux/init.h>
  23. #include <linux/io.h>
  24. #include <linux/pci.h>
  25. #include <asm/cpu_device_id.h>
  26. #include <asm/intel-family.h>
  27. #include <asm/pmc_core.h>
  28. #include "intel_pmc_core.h"
  29. static struct pmc_dev pmc;
  30. static const struct pci_device_id pmc_pci_ids[] = {
  31. { PCI_VDEVICE(INTEL, SPT_PMC_PCI_DEVICE_ID), (kernel_ulong_t)NULL },
  32. { 0, },
  33. };
  34. static inline u32 pmc_core_reg_read(struct pmc_dev *pmcdev, int reg_offset)
  35. {
  36. return readl(pmcdev->regbase + reg_offset);
  37. }
  38. static inline u32 pmc_core_adjust_slp_s0_step(u32 value)
  39. {
  40. return value * SPT_PMC_SLP_S0_RES_COUNTER_STEP;
  41. }
  42. /**
  43. * intel_pmc_slp_s0_counter_read() - Read SLP_S0 residency.
  44. * @data: Out param that contains current SLP_S0 count.
  45. *
  46. * This API currently supports Intel Skylake SoC and Sunrise
  47. * Point Platform Controller Hub. Future platform support
  48. * should be added for platforms that support low power modes
  49. * beyond Package C10 state.
  50. *
  51. * SLP_S0_RESIDENCY counter counts in 100 us granularity per
  52. * step hence function populates the multiplied value in out
  53. * parameter @data.
  54. *
  55. * Return: an error code or 0 on success.
  56. */
  57. int intel_pmc_slp_s0_counter_read(u32 *data)
  58. {
  59. struct pmc_dev *pmcdev = &pmc;
  60. u32 value;
  61. if (!pmcdev->has_slp_s0_res)
  62. return -EACCES;
  63. value = pmc_core_reg_read(pmcdev, SPT_PMC_SLP_S0_RES_COUNTER_OFFSET);
  64. *data = pmc_core_adjust_slp_s0_step(value);
  65. return 0;
  66. }
  67. EXPORT_SYMBOL_GPL(intel_pmc_slp_s0_counter_read);
  68. static int pmc_core_dev_state_get(void *data, u64 *val)
  69. {
  70. struct pmc_dev *pmcdev = data;
  71. u32 value;
  72. value = pmc_core_reg_read(pmcdev, SPT_PMC_SLP_S0_RES_COUNTER_OFFSET);
  73. *val = pmc_core_adjust_slp_s0_step(value);
  74. return 0;
  75. }
  76. DEFINE_DEBUGFS_ATTRIBUTE(pmc_core_dev_state, pmc_core_dev_state_get, NULL, "%llu\n");
  77. static void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev)
  78. {
  79. debugfs_remove_recursive(pmcdev->dbgfs_dir);
  80. }
  81. static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
  82. {
  83. struct dentry *dir, *file;
  84. dir = debugfs_create_dir("pmc_core", NULL);
  85. if (!dir)
  86. return -ENOMEM;
  87. pmcdev->dbgfs_dir = dir;
  88. file = debugfs_create_file("slp_s0_residency_usec", S_IFREG | S_IRUGO,
  89. dir, pmcdev, &pmc_core_dev_state);
  90. if (!file) {
  91. pmc_core_dbgfs_unregister(pmcdev);
  92. return -ENODEV;
  93. }
  94. return 0;
  95. }
  96. static const struct x86_cpu_id intel_pmc_core_ids[] = {
  97. { X86_VENDOR_INTEL, 6, INTEL_FAM6_SKYLAKE_MOBILE, X86_FEATURE_MWAIT,
  98. (kernel_ulong_t)NULL},
  99. { X86_VENDOR_INTEL, 6, INTEL_FAM6_SKYLAKE_DESKTOP, X86_FEATURE_MWAIT,
  100. (kernel_ulong_t)NULL},
  101. {}
  102. };
  103. static int pmc_core_probe(struct pci_dev *dev, const struct pci_device_id *id)
  104. {
  105. struct device *ptr_dev = &dev->dev;
  106. struct pmc_dev *pmcdev = &pmc;
  107. const struct x86_cpu_id *cpu_id;
  108. int err;
  109. cpu_id = x86_match_cpu(intel_pmc_core_ids);
  110. if (!cpu_id) {
  111. dev_dbg(&dev->dev, "PMC Core: cpuid mismatch.\n");
  112. return -EINVAL;
  113. }
  114. err = pcim_enable_device(dev);
  115. if (err < 0) {
  116. dev_dbg(&dev->dev, "PMC Core: failed to enable Power Management Controller.\n");
  117. return err;
  118. }
  119. err = pci_read_config_dword(dev,
  120. SPT_PMC_BASE_ADDR_OFFSET,
  121. &pmcdev->base_addr);
  122. if (err < 0) {
  123. dev_dbg(&dev->dev, "PMC Core: failed to read PCI config space.\n");
  124. return err;
  125. }
  126. dev_dbg(&dev->dev, "PMC Core: PWRMBASE is %#x\n", pmcdev->base_addr);
  127. pmcdev->regbase = devm_ioremap_nocache(ptr_dev,
  128. pmcdev->base_addr,
  129. SPT_PMC_MMIO_REG_LEN);
  130. if (!pmcdev->regbase) {
  131. dev_dbg(&dev->dev, "PMC Core: ioremap failed.\n");
  132. return -ENOMEM;
  133. }
  134. err = pmc_core_dbgfs_register(pmcdev);
  135. if (err < 0)
  136. dev_warn(&dev->dev, "PMC Core: debugfs register failed.\n");
  137. pmc.has_slp_s0_res = true;
  138. return 0;
  139. }
  140. static struct pci_driver intel_pmc_core_driver = {
  141. .name = "intel_pmc_core",
  142. .id_table = pmc_pci_ids,
  143. .probe = pmc_core_probe,
  144. };
  145. builtin_pci_driver(intel_pmc_core_driver);