ptm.c 3.7 KB

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