1 // SPDX-License-Identifier: GPL-2.0
3 * PCI Express Precision Time Measurement
4 * Copyright (c) 2016, Intel Corporation.
7 #include <linux/module.h>
8 #include <linux/init.h>
13 * If the next upstream device supports PTM, return it; otherwise return
14 * NULL. PTM Messages are local, so both link partners must support it.
16 static struct pci_dev *pci_upstream_ptm(struct pci_dev *dev)
18 struct pci_dev *ups = pci_upstream_bridge(dev);
21 * Switch Downstream Ports are not permitted to have a PTM
22 * capability; their PTM behavior is controlled by the Upstream
23 * Port (PCIe r5.0, sec 7.9.16), so if the upstream bridge is a
24 * Switch Downstream Port, look up one more level.
26 if (ups && pci_pcie_type(ups) == PCI_EXP_TYPE_DOWNSTREAM)
27 ups = pci_upstream_bridge(ups);
29 if (ups && ups->ptm_cap)
36 * Find the PTM Capability (if present) and extract the information we need
39 void pci_ptm_init(struct pci_dev *dev)
45 if (!pci_is_pcie(dev))
48 ptm = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_PTM);
53 pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_PTM, sizeof(u32));
55 pci_read_config_dword(dev, ptm + PCI_PTM_CAP, &cap);
56 dev->ptm_granularity = (cap & PCI_PTM_GRANULARITY_MASK) >> 8;
59 * Per the spec recommendation (PCIe r6.0, sec 7.9.15.3), select the
60 * furthest upstream Time Source as the PTM Root. For Endpoints,
61 * "the Effective Granularity is the maximum Local Clock Granularity
62 * reported by the PTM Root and all intervening PTM Time Sources."
64 ups = pci_upstream_ptm(dev);
66 if (ups->ptm_granularity == 0)
67 dev->ptm_granularity = 0;
68 else if (ups->ptm_granularity > dev->ptm_granularity)
69 dev->ptm_granularity = ups->ptm_granularity;
70 } else if (cap & PCI_PTM_CAP_ROOT) {
72 } else if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) {
75 * Per sec 7.9.15.3, this should be the Local Clock
76 * Granularity of the associated Time Source. But it
77 * doesn't say how to find that Time Source.
79 dev->ptm_granularity = 0;
82 if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
83 pci_pcie_type(dev) == PCI_EXP_TYPE_UPSTREAM)
84 pci_enable_ptm(dev, NULL);
87 void pci_save_ptm_state(struct pci_dev *dev)
89 u16 ptm = dev->ptm_cap;
90 struct pci_cap_saved_state *save_state;
96 save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_PTM);
100 cap = (u32 *)&save_state->cap.data[0];
101 pci_read_config_dword(dev, ptm + PCI_PTM_CTRL, cap);
104 void pci_restore_ptm_state(struct pci_dev *dev)
106 u16 ptm = dev->ptm_cap;
107 struct pci_cap_saved_state *save_state;
113 save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_PTM);
117 cap = (u32 *)&save_state->cap.data[0];
118 pci_write_config_dword(dev, ptm + PCI_PTM_CTRL, *cap);
121 /* Enable PTM in the Control register if possible */
122 static int __pci_enable_ptm(struct pci_dev *dev)
124 u16 ptm = dev->ptm_cap;
132 * A device uses local PTM Messages to request time information
133 * from a PTM Root that's farther upstream. Every device along the
134 * path must support PTM and have it enabled so it can handle the
135 * messages. Therefore, if this device is not a PTM Root, the
136 * upstream link partner must have PTM enabled before we can enable
139 if (!dev->ptm_root) {
140 ups = pci_upstream_ptm(dev);
141 if (!ups || !ups->ptm_enabled)
145 pci_read_config_dword(dev, ptm + PCI_PTM_CTRL, &ctrl);
147 ctrl |= PCI_PTM_CTRL_ENABLE;
148 ctrl &= ~PCI_PTM_GRANULARITY_MASK;
149 ctrl |= dev->ptm_granularity << 8;
151 ctrl |= PCI_PTM_CTRL_ROOT;
153 pci_write_config_dword(dev, ptm + PCI_PTM_CTRL, ctrl);
158 * pci_enable_ptm() - Enable Precision Time Measurement
160 * @granularity: pointer to return granularity
162 * Enable Precision Time Measurement for @dev. If successful and
163 * @granularity is non-NULL, return the Effective Granularity.
165 * Return: zero if successful, or -EINVAL if @dev lacks a PTM Capability or
166 * is not a PTM Root and lacks an upstream path of PTM-enabled devices.
168 int pci_enable_ptm(struct pci_dev *dev, u8 *granularity)
173 rc = __pci_enable_ptm(dev);
177 dev->ptm_enabled = 1;
180 *granularity = dev->ptm_granularity;
182 switch (dev->ptm_granularity) {
184 snprintf(clock_desc, sizeof(clock_desc), "unknown");
187 snprintf(clock_desc, sizeof(clock_desc), ">254ns");
190 snprintf(clock_desc, sizeof(clock_desc), "%uns",
191 dev->ptm_granularity);
194 pci_info(dev, "PTM enabled%s, %s granularity\n",
195 dev->ptm_root ? " (root)" : "", clock_desc);
199 EXPORT_SYMBOL(pci_enable_ptm);
201 static void __pci_disable_ptm(struct pci_dev *dev)
203 u16 ptm = dev->ptm_cap;
209 pci_read_config_dword(dev, ptm + PCI_PTM_CTRL, &ctrl);
210 ctrl &= ~(PCI_PTM_CTRL_ENABLE | PCI_PTM_CTRL_ROOT);
211 pci_write_config_dword(dev, ptm + PCI_PTM_CTRL, ctrl);
215 * pci_disable_ptm() - Disable Precision Time Measurement
218 * Disable Precision Time Measurement for @dev.
220 void pci_disable_ptm(struct pci_dev *dev)
222 if (dev->ptm_enabled) {
223 __pci_disable_ptm(dev);
224 dev->ptm_enabled = 0;
227 EXPORT_SYMBOL(pci_disable_ptm);
230 * Disable PTM, but preserve dev->ptm_enabled so we silently re-enable it on
231 * resume if necessary.
233 void pci_suspend_ptm(struct pci_dev *dev)
235 if (dev->ptm_enabled)
236 __pci_disable_ptm(dev);
239 /* If PTM was enabled before suspend, re-enable it when resuming */
240 void pci_resume_ptm(struct pci_dev *dev)
242 if (dev->ptm_enabled)
243 __pci_enable_ptm(dev);
246 bool pcie_ptm_enabled(struct pci_dev *dev)
251 return dev->ptm_enabled;
253 EXPORT_SYMBOL(pcie_ptm_enabled);