]>
Commit | Line | Data |
---|---|---|
9bb04a0c JY |
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 | { | |
8b2ec318 BH |
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); | |
9bb04a0c JY |
38 | } |
39 | ||
40 | void pci_ptm_init(struct pci_dev *dev) | |
41 | { | |
42 | int pos; | |
43 | u32 cap, ctrl; | |
8b2ec318 | 44 | u8 local_clock; |
9bb04a0c JY |
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); | |
8b2ec318 | 64 | local_clock = (cap & PCI_PTM_GRANULARITY_MASK) >> 8; |
9bb04a0c JY |
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; | |
8b2ec318 BH |
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; | |
9bb04a0c JY |
79 | } else { |
80 | if (cap & PCI_PTM_CAP_ROOT) { | |
81 | ctrl = PCI_PTM_CTRL_ENABLE | PCI_PTM_CTRL_ROOT; | |
82 | dev->ptm_root = 1; | |
8b2ec318 | 83 | dev->ptm_granularity = local_clock; |
9bb04a0c JY |
84 | } else |
85 | return; | |
86 | } | |
87 | ||
8b2ec318 | 88 | ctrl |= dev->ptm_granularity << 8; |
9bb04a0c JY |
89 | pci_write_config_dword(dev, pos + PCI_PTM_CTRL, ctrl); |
90 | dev->ptm_enabled = 1; | |
91 | ||
92 | pci_ptm_info(dev); | |
93 | } | |
eec097d4 BH |
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; | |
8b2ec318 BH |
124 | |
125 | dev->ptm_granularity = ups->ptm_granularity; | |
eec097d4 | 126 | } else if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) { |
8b2ec318 | 127 | dev->ptm_granularity = 0; |
eec097d4 BH |
128 | } else |
129 | return -EINVAL; | |
130 | ||
131 | ctrl = PCI_PTM_CTRL_ENABLE; | |
8b2ec318 | 132 | ctrl |= dev->ptm_granularity << 8; |
eec097d4 BH |
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) | |
8b2ec318 | 139 | *granularity = dev->ptm_granularity; |
eec097d4 BH |
140 | return 0; |
141 | } | |
142 | EXPORT_SYMBOL(pci_enable_ptm); |