1 // SPDX-License-Identifier: GPL-2.0
3 * PCI Express Precision Time Measurement
4 * Copyright (c) 2016, Intel Corporation.
7 #include <linux/bitfield.h>
8 #include <linux/module.h>
9 #include <linux/init.h>
10 #include <linux/pci.h>
14 * If the next upstream device supports PTM, return it; otherwise return
15 * NULL. PTM Messages are local, so both link partners must support it.
17 static struct pci_dev
*pci_upstream_ptm(struct pci_dev
*dev
)
19 struct pci_dev
*ups
= pci_upstream_bridge(dev
);
22 * Switch Downstream Ports are not permitted to have a PTM
23 * capability; their PTM behavior is controlled by the Upstream
24 * Port (PCIe r5.0, sec 7.9.16), so if the upstream bridge is a
25 * Switch Downstream Port, look up one more level.
27 if (ups
&& pci_pcie_type(ups
) == PCI_EXP_TYPE_DOWNSTREAM
)
28 ups
= pci_upstream_bridge(ups
);
30 if (ups
&& ups
->ptm_cap
)
37 * Find the PTM Capability (if present) and extract the information we need
40 void pci_ptm_init(struct pci_dev
*dev
)
46 if (!pci_is_pcie(dev
))
49 ptm
= pci_find_ext_capability(dev
, PCI_EXT_CAP_ID_PTM
);
54 pci_add_ext_cap_save_buffer(dev
, PCI_EXT_CAP_ID_PTM
, sizeof(u32
));
56 pci_read_config_dword(dev
, ptm
+ PCI_PTM_CAP
, &cap
);
57 dev
->ptm_granularity
= FIELD_GET(PCI_PTM_GRANULARITY_MASK
, cap
);
60 * Per the spec recommendation (PCIe r6.0, sec 7.9.15.3), select the
61 * furthest upstream Time Source as the PTM Root. For Endpoints,
62 * "the Effective Granularity is the maximum Local Clock Granularity
63 * reported by the PTM Root and all intervening PTM Time Sources."
65 ups
= pci_upstream_ptm(dev
);
67 if (ups
->ptm_granularity
== 0)
68 dev
->ptm_granularity
= 0;
69 else if (ups
->ptm_granularity
> dev
->ptm_granularity
)
70 dev
->ptm_granularity
= ups
->ptm_granularity
;
71 } else if (cap
& PCI_PTM_CAP_ROOT
) {
73 } else if (pci_pcie_type(dev
) == PCI_EXP_TYPE_RC_END
) {
76 * Per sec 7.9.15.3, this should be the Local Clock
77 * Granularity of the associated Time Source. But it
78 * doesn't say how to find that Time Source.
80 dev
->ptm_granularity
= 0;
83 if (pci_pcie_type(dev
) == PCI_EXP_TYPE_ROOT_PORT
||
84 pci_pcie_type(dev
) == PCI_EXP_TYPE_UPSTREAM
)
85 pci_enable_ptm(dev
, NULL
);
88 void pci_save_ptm_state(struct pci_dev
*dev
)
90 u16 ptm
= dev
->ptm_cap
;
91 struct pci_cap_saved_state
*save_state
;
97 save_state
= pci_find_saved_ext_cap(dev
, PCI_EXT_CAP_ID_PTM
);
101 cap
= (u32
*)&save_state
->cap
.data
[0];
102 pci_read_config_dword(dev
, ptm
+ PCI_PTM_CTRL
, cap
);
105 void pci_restore_ptm_state(struct pci_dev
*dev
)
107 u16 ptm
= dev
->ptm_cap
;
108 struct pci_cap_saved_state
*save_state
;
114 save_state
= pci_find_saved_ext_cap(dev
, PCI_EXT_CAP_ID_PTM
);
118 cap
= (u32
*)&save_state
->cap
.data
[0];
119 pci_write_config_dword(dev
, ptm
+ PCI_PTM_CTRL
, *cap
);
122 /* Enable PTM in the Control register if possible */
123 static int __pci_enable_ptm(struct pci_dev
*dev
)
125 u16 ptm
= dev
->ptm_cap
;
133 * A device uses local PTM Messages to request time information
134 * from a PTM Root that's farther upstream. Every device along the
135 * path must support PTM and have it enabled so it can handle the
136 * messages. Therefore, if this device is not a PTM Root, the
137 * upstream link partner must have PTM enabled before we can enable
140 if (!dev
->ptm_root
) {
141 ups
= pci_upstream_ptm(dev
);
142 if (!ups
|| !ups
->ptm_enabled
)
146 pci_read_config_dword(dev
, ptm
+ PCI_PTM_CTRL
, &ctrl
);
148 ctrl
|= PCI_PTM_CTRL_ENABLE
;
149 ctrl
&= ~PCI_PTM_GRANULARITY_MASK
;
150 ctrl
|= FIELD_PREP(PCI_PTM_GRANULARITY_MASK
, dev
->ptm_granularity
);
152 ctrl
|= PCI_PTM_CTRL_ROOT
;
154 pci_write_config_dword(dev
, ptm
+ PCI_PTM_CTRL
, ctrl
);
159 * pci_enable_ptm() - Enable Precision Time Measurement
161 * @granularity: pointer to return granularity
163 * Enable Precision Time Measurement for @dev. If successful and
164 * @granularity is non-NULL, return the Effective Granularity.
166 * Return: zero if successful, or -EINVAL if @dev lacks a PTM Capability or
167 * is not a PTM Root and lacks an upstream path of PTM-enabled devices.
169 int pci_enable_ptm(struct pci_dev
*dev
, u8
*granularity
)
174 rc
= __pci_enable_ptm(dev
);
178 dev
->ptm_enabled
= 1;
181 *granularity
= dev
->ptm_granularity
;
183 switch (dev
->ptm_granularity
) {
185 snprintf(clock_desc
, sizeof(clock_desc
), "unknown");
188 snprintf(clock_desc
, sizeof(clock_desc
), ">254ns");
191 snprintf(clock_desc
, sizeof(clock_desc
), "%uns",
192 dev
->ptm_granularity
);
195 pci_info(dev
, "PTM enabled%s, %s granularity\n",
196 dev
->ptm_root
? " (root)" : "", clock_desc
);
200 EXPORT_SYMBOL(pci_enable_ptm
);
202 static void __pci_disable_ptm(struct pci_dev
*dev
)
204 u16 ptm
= dev
->ptm_cap
;
210 pci_read_config_dword(dev
, ptm
+ PCI_PTM_CTRL
, &ctrl
);
211 ctrl
&= ~(PCI_PTM_CTRL_ENABLE
| PCI_PTM_CTRL_ROOT
);
212 pci_write_config_dword(dev
, ptm
+ PCI_PTM_CTRL
, ctrl
);
216 * pci_disable_ptm() - Disable Precision Time Measurement
219 * Disable Precision Time Measurement for @dev.
221 void pci_disable_ptm(struct pci_dev
*dev
)
223 if (dev
->ptm_enabled
) {
224 __pci_disable_ptm(dev
);
225 dev
->ptm_enabled
= 0;
228 EXPORT_SYMBOL(pci_disable_ptm
);
231 * Disable PTM, but preserve dev->ptm_enabled so we silently re-enable it on
232 * resume if necessary.
234 void pci_suspend_ptm(struct pci_dev
*dev
)
236 if (dev
->ptm_enabled
)
237 __pci_disable_ptm(dev
);
240 /* If PTM was enabled before suspend, re-enable it when resuming */
241 void pci_resume_ptm(struct pci_dev
*dev
)
243 if (dev
->ptm_enabled
)
244 __pci_enable_ptm(dev
);
247 bool pcie_ptm_enabled(struct pci_dev
*dev
)
252 return dev
->ptm_enabled
;
254 EXPORT_SYMBOL(pcie_ptm_enabled
);