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