1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * PCI Express Precision Time Measurement 4 * Copyright (c) 2016, Intel Corporation. 5 */ 6 7 #include <linux/module.h> 8 #include <linux/init.h> 9 #include <linux/pci.h> 10 #include "../pci.h" 11 12 /* 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. 15 */ 16 static struct pci_dev *pci_upstream_ptm(struct pci_dev *dev) 17 { 18 struct pci_dev *ups = pci_upstream_bridge(dev); 19 20 /* 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. 25 */ 26 if (ups && pci_pcie_type(ups) == PCI_EXP_TYPE_DOWNSTREAM) 27 ups = pci_upstream_bridge(ups); 28 29 if (ups && ups->ptm_cap) 30 return ups; 31 32 return NULL; 33 } 34 35 /* 36 * Find the PTM Capability (if present) and extract the information we need 37 * to use it. 38 */ 39 void pci_ptm_init(struct pci_dev *dev) 40 { 41 u16 ptm; 42 u32 cap; 43 struct pci_dev *ups; 44 45 if (!pci_is_pcie(dev)) 46 return; 47 48 ptm = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_PTM); 49 if (!ptm) 50 return; 51 52 dev->ptm_cap = ptm; 53 pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_PTM, sizeof(u32)); 54 55 pci_read_config_dword(dev, ptm + PCI_PTM_CAP, &cap); 56 dev->ptm_granularity = (cap & PCI_PTM_GRANULARITY_MASK) >> 8; 57 58 /* 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." 63 */ 64 ups = pci_upstream_ptm(dev); 65 if (ups) { 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) { 71 dev->ptm_root = 1; 72 } else if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) { 73 74 /* 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. 78 */ 79 dev->ptm_granularity = 0; 80 } 81 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); 85 } 86 87 void pci_save_ptm_state(struct pci_dev *dev) 88 { 89 u16 ptm = dev->ptm_cap; 90 struct pci_cap_saved_state *save_state; 91 u32 *cap; 92 93 if (!ptm) 94 return; 95 96 save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_PTM); 97 if (!save_state) 98 return; 99 100 cap = (u32 *)&save_state->cap.data[0]; 101 pci_read_config_dword(dev, ptm + PCI_PTM_CTRL, cap); 102 } 103 104 void pci_restore_ptm_state(struct pci_dev *dev) 105 { 106 u16 ptm = dev->ptm_cap; 107 struct pci_cap_saved_state *save_state; 108 u32 *cap; 109 110 if (!ptm) 111 return; 112 113 save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_PTM); 114 if (!save_state) 115 return; 116 117 cap = (u32 *)&save_state->cap.data[0]; 118 pci_write_config_dword(dev, ptm + PCI_PTM_CTRL, *cap); 119 } 120 121 /* Enable PTM in the Control register if possible */ 122 static int __pci_enable_ptm(struct pci_dev *dev) 123 { 124 u16 ptm = dev->ptm_cap; 125 struct pci_dev *ups; 126 u32 ctrl; 127 128 if (!ptm) 129 return -EINVAL; 130 131 /* 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 137 * PTM. 138 */ 139 if (!dev->ptm_root) { 140 ups = pci_upstream_ptm(dev); 141 if (!ups || !ups->ptm_enabled) 142 return -EINVAL; 143 } 144 145 pci_read_config_dword(dev, ptm + PCI_PTM_CTRL, &ctrl); 146 147 ctrl |= PCI_PTM_CTRL_ENABLE; 148 ctrl &= ~PCI_PTM_GRANULARITY_MASK; 149 ctrl |= dev->ptm_granularity << 8; 150 if (dev->ptm_root) 151 ctrl |= PCI_PTM_CTRL_ROOT; 152 153 pci_write_config_dword(dev, ptm + PCI_PTM_CTRL, ctrl); 154 return 0; 155 } 156 157 /** 158 * pci_enable_ptm() - Enable Precision Time Measurement 159 * @dev: PCI device 160 * @granularity: pointer to return granularity 161 * 162 * Enable Precision Time Measurement for @dev. If successful and 163 * @granularity is non-NULL, return the Effective Granularity. 164 * 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. 167 */ 168 int pci_enable_ptm(struct pci_dev *dev, u8 *granularity) 169 { 170 int rc; 171 char clock_desc[8]; 172 173 rc = __pci_enable_ptm(dev); 174 if (rc) 175 return rc; 176 177 dev->ptm_enabled = 1; 178 179 if (granularity) 180 *granularity = dev->ptm_granularity; 181 182 switch (dev->ptm_granularity) { 183 case 0: 184 snprintf(clock_desc, sizeof(clock_desc), "unknown"); 185 break; 186 case 255: 187 snprintf(clock_desc, sizeof(clock_desc), ">254ns"); 188 break; 189 default: 190 snprintf(clock_desc, sizeof(clock_desc), "%uns", 191 dev->ptm_granularity); 192 break; 193 } 194 pci_info(dev, "PTM enabled%s, %s granularity\n", 195 dev->ptm_root ? " (root)" : "", clock_desc); 196 197 return 0; 198 } 199 EXPORT_SYMBOL(pci_enable_ptm); 200 201 static void __pci_disable_ptm(struct pci_dev *dev) 202 { 203 u16 ptm = dev->ptm_cap; 204 u32 ctrl; 205 206 if (!ptm) 207 return; 208 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); 212 } 213 214 /** 215 * pci_disable_ptm() - Disable Precision Time Measurement 216 * @dev: PCI device 217 * 218 * Disable Precision Time Measurement for @dev. 219 */ 220 void pci_disable_ptm(struct pci_dev *dev) 221 { 222 if (dev->ptm_enabled) { 223 __pci_disable_ptm(dev); 224 dev->ptm_enabled = 0; 225 } 226 } 227 EXPORT_SYMBOL(pci_disable_ptm); 228 229 /* 230 * Disable PTM, but preserve dev->ptm_enabled so we silently re-enable it on 231 * resume if necessary. 232 */ 233 void pci_suspend_ptm(struct pci_dev *dev) 234 { 235 if (dev->ptm_enabled) 236 __pci_disable_ptm(dev); 237 } 238 239 /* If PTM was enabled before suspend, re-enable it when resuming */ 240 void pci_resume_ptm(struct pci_dev *dev) 241 { 242 if (dev->ptm_enabled) 243 __pci_enable_ptm(dev); 244 } 245 246 bool pcie_ptm_enabled(struct pci_dev *dev) 247 { 248 if (!dev) 249 return false; 250 251 return dev->ptm_enabled; 252 } 253 EXPORT_SYMBOL(pcie_ptm_enabled); 254