ptm.c 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * PCI Express Precision Time Measurement
  4. * Copyright (c) 2016, Intel Corporation.
  5. */
  6. #include <linux/module.h>
  7. #include <linux/init.h>
  8. #include <linux/pci.h>
  9. #include "../pci.h"
  10. static void pci_ptm_info(struct pci_dev *dev)
  11. {
  12. char clock_desc[8];
  13. switch (dev->ptm_granularity) {
  14. case 0:
  15. snprintf(clock_desc, sizeof(clock_desc), "unknown");
  16. break;
  17. case 255:
  18. snprintf(clock_desc, sizeof(clock_desc), ">254ns");
  19. break;
  20. default:
  21. snprintf(clock_desc, sizeof(clock_desc), "%udns",
  22. dev->ptm_granularity);
  23. break;
  24. }
  25. pci_info(dev, "PTM enabled%s, %s granularity\n",
  26. dev->ptm_root ? " (root)" : "", clock_desc);
  27. }
  28. void pci_ptm_init(struct pci_dev *dev)
  29. {
  30. int pos;
  31. u32 cap, ctrl;
  32. u8 local_clock;
  33. struct pci_dev *ups;
  34. if (!pci_is_pcie(dev))
  35. return;
  36. pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_PTM);
  37. if (!pos)
  38. return;
  39. /*
  40. * Enable PTM only on interior devices (root ports, switch ports,
  41. * etc.) on the assumption that it causes no link traffic until an
  42. * endpoint enables it.
  43. */
  44. if ((pci_pcie_type(dev) == PCI_EXP_TYPE_ENDPOINT ||
  45. pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END))
  46. return;
  47. pci_read_config_dword(dev, pos + PCI_PTM_CAP, &cap);
  48. local_clock = (cap & PCI_PTM_GRANULARITY_MASK) >> 8;
  49. /*
  50. * There's no point in enabling PTM unless it's enabled in the
  51. * upstream device or this device can be a PTM Root itself. Per
  52. * the spec recommendation (PCIe r3.1, sec 7.32.3), select the
  53. * furthest upstream Time Source as the PTM Root.
  54. */
  55. ups = pci_upstream_bridge(dev);
  56. if (ups && ups->ptm_enabled) {
  57. ctrl = PCI_PTM_CTRL_ENABLE;
  58. if (ups->ptm_granularity == 0)
  59. dev->ptm_granularity = 0;
  60. else if (ups->ptm_granularity > local_clock)
  61. dev->ptm_granularity = ups->ptm_granularity;
  62. } else {
  63. if (cap & PCI_PTM_CAP_ROOT) {
  64. ctrl = PCI_PTM_CTRL_ENABLE | PCI_PTM_CTRL_ROOT;
  65. dev->ptm_root = 1;
  66. dev->ptm_granularity = local_clock;
  67. } else
  68. return;
  69. }
  70. ctrl |= dev->ptm_granularity << 8;
  71. pci_write_config_dword(dev, pos + PCI_PTM_CTRL, ctrl);
  72. dev->ptm_enabled = 1;
  73. pci_ptm_info(dev);
  74. }
  75. int pci_enable_ptm(struct pci_dev *dev, u8 *granularity)
  76. {
  77. int pos;
  78. u32 cap, ctrl;
  79. struct pci_dev *ups;
  80. if (!pci_is_pcie(dev))
  81. return -EINVAL;
  82. pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_PTM);
  83. if (!pos)
  84. return -EINVAL;
  85. pci_read_config_dword(dev, pos + PCI_PTM_CAP, &cap);
  86. if (!(cap & PCI_PTM_CAP_REQ))
  87. return -EINVAL;
  88. /*
  89. * For a PCIe Endpoint, PTM is only useful if the endpoint can
  90. * issue PTM requests to upstream devices that have PTM enabled.
  91. *
  92. * For Root Complex Integrated Endpoints, there is no upstream
  93. * device, so there must be some implementation-specific way to
  94. * associate the endpoint with a time source.
  95. */
  96. if (pci_pcie_type(dev) == PCI_EXP_TYPE_ENDPOINT) {
  97. ups = pci_upstream_bridge(dev);
  98. if (!ups || !ups->ptm_enabled)
  99. return -EINVAL;
  100. dev->ptm_granularity = ups->ptm_granularity;
  101. } else if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) {
  102. dev->ptm_granularity = 0;
  103. } else
  104. return -EINVAL;
  105. ctrl = PCI_PTM_CTRL_ENABLE;
  106. ctrl |= dev->ptm_granularity << 8;
  107. pci_write_config_dword(dev, pos + PCI_PTM_CTRL, ctrl);
  108. dev->ptm_enabled = 1;
  109. pci_ptm_info(dev);
  110. if (granularity)
  111. *granularity = dev->ptm_granularity;
  112. return 0;
  113. }
  114. EXPORT_SYMBOL(pci_enable_ptm);