aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/pci/pci.h6
-rw-r--r--drivers/pci/pcie/Kconfig11
-rw-r--r--drivers/pci/pcie/Makefile1
-rw-r--r--drivers/pci/pcie/ptm.c142
-rw-r--r--drivers/pci/probe.c3
-rw-r--r--include/linux/pci.h13
-rw-r--r--include/uapi/linux/pci_regs.h12
7 files changed, 187 insertions, 1 deletions
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 9730c474b016..194521bfb1a3 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -332,6 +332,12 @@ static inline resource_size_t pci_resource_alignment(struct pci_dev *dev,
332 332
333void pci_enable_acs(struct pci_dev *dev); 333void pci_enable_acs(struct pci_dev *dev);
334 334
335#ifdef CONFIG_PCIE_PTM
336void pci_ptm_init(struct pci_dev *dev);
337#else
338static inline void pci_ptm_init(struct pci_dev *dev) { }
339#endif
340
335struct pci_dev_reset_methods { 341struct pci_dev_reset_methods {
336 u16 vendor; 342 u16 vendor;
337 u16 device; 343 u16 device;
diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig
index 7fcea75afa4c..7ce77635e5ad 100644
--- a/drivers/pci/pcie/Kconfig
+++ b/drivers/pci/pcie/Kconfig
@@ -92,3 +92,14 @@ config PCIE_DPC
92 will be handled by the DPC driver. If your system doesn't 92 will be handled by the DPC driver. If your system doesn't
93 have this capability or you do not want to use this feature, 93 have this capability or you do not want to use this feature,
94 it is safe to answer N. 94 it is safe to answer N.
95
96config PCIE_PTM
97 bool "PCIe Precision Time Measurement support"
98 default n
99 depends on PCIEPORTBUS
100 help
101 This enables PCI Express Precision Time Measurement (PTM)
102 support.
103
104 This is only useful if you have devices that support PTM, but it
105 is safe to enable even if you don't.
diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile
index b24525b3dec1..36e35ea8fde7 100644
--- a/drivers/pci/pcie/Makefile
+++ b/drivers/pci/pcie/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_PCIEAER) += aer/
16obj-$(CONFIG_PCIE_PME) += pme.o 16obj-$(CONFIG_PCIE_PME) += pme.o
17 17
18obj-$(CONFIG_PCIE_DPC) += pcie-dpc.o 18obj-$(CONFIG_PCIE_DPC) += pcie-dpc.o
19obj-$(CONFIG_PCIE_PTM) += ptm.o
diff --git a/drivers/pci/pcie/ptm.c b/drivers/pci/pcie/ptm.c
new file mode 100644
index 000000000000..bab8ac63c4f3
--- /dev/null
+++ b/drivers/pci/pcie/ptm.c
@@ -0,0 +1,142 @@
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
20static 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
40void 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
95int 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}
142EXPORT_SYMBOL(pci_enable_ptm);
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 93f280df3428..e2e424472058 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1667,6 +1667,9 @@ static void pci_init_capabilities(struct pci_dev *dev)
1667 pci_enable_acs(dev); 1667 pci_enable_acs(dev);
1668 1668
1669 pci_cleanup_aer_error_status_regs(dev); 1669 pci_cleanup_aer_error_status_regs(dev);
1670
1671 /* Precision Time Measurement */
1672 pci_ptm_init(dev);
1670} 1673}
1671 1674
1672/* 1675/*
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 2599a980340f..7256f33b6a15 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -367,6 +367,12 @@ struct pci_dev {
367 int rom_attr_enabled; /* has display of the rom attribute been enabled? */ 367 int rom_attr_enabled; /* has display of the rom attribute been enabled? */
368 struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* sysfs file for resources */ 368 struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* sysfs file for resources */
369 struct bin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE]; /* sysfs file for WC mapping of resources */ 369 struct bin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE]; /* sysfs file for WC mapping of resources */
370
371#ifdef CONFIG_PCIE_PTM
372 unsigned int ptm_root:1;
373 unsigned int ptm_enabled:1;
374 u8 ptm_granularity;
375#endif
370#ifdef CONFIG_PCI_MSI 376#ifdef CONFIG_PCI_MSI
371 const struct attribute_group **msi_irq_groups; 377 const struct attribute_group **msi_irq_groups;
372#endif 378#endif
@@ -1402,6 +1408,13 @@ static inline void pci_disable_ats(struct pci_dev *d) { }
1402static inline int pci_ats_queue_depth(struct pci_dev *d) { return -ENODEV; } 1408static inline int pci_ats_queue_depth(struct pci_dev *d) { return -ENODEV; }
1403#endif 1409#endif
1404 1410
1411#ifdef CONFIG_PCIE_PTM
1412int pci_enable_ptm(struct pci_dev *dev, u8 *granularity);
1413#else
1414static inline int pci_enable_ptm(struct pci_dev *dev, u8 *granularity)
1415{ return -EINVAL; }
1416#endif
1417
1405void pci_cfg_access_lock(struct pci_dev *dev); 1418void pci_cfg_access_lock(struct pci_dev *dev);
1406bool pci_cfg_access_trylock(struct pci_dev *dev); 1419bool pci_cfg_access_trylock(struct pci_dev *dev);
1407void pci_cfg_access_unlock(struct pci_dev *dev); 1420void pci_cfg_access_unlock(struct pci_dev *dev);
diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h
index 404095124ae2..d812172d1d7b 100644
--- a/include/uapi/linux/pci_regs.h
+++ b/include/uapi/linux/pci_regs.h
@@ -671,7 +671,8 @@
671#define PCI_EXT_CAP_ID_PMUX 0x1A /* Protocol Multiplexing */ 671#define PCI_EXT_CAP_ID_PMUX 0x1A /* Protocol Multiplexing */
672#define PCI_EXT_CAP_ID_PASID 0x1B /* Process Address Space ID */ 672#define PCI_EXT_CAP_ID_PASID 0x1B /* Process Address Space ID */
673#define PCI_EXT_CAP_ID_DPC 0x1D /* Downstream Port Containment */ 673#define PCI_EXT_CAP_ID_DPC 0x1D /* Downstream Port Containment */
674#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_DPC 674#define PCI_EXT_CAP_ID_PTM 0x1F /* Precision Time Measurement */
675#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PTM
675 676
676#define PCI_EXT_CAP_DSN_SIZEOF 12 677#define PCI_EXT_CAP_DSN_SIZEOF 12
677#define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40 678#define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40
@@ -964,4 +965,13 @@
964 965
965#define PCI_EXP_DPC_SOURCE_ID 10 /* DPC Source Identifier */ 966#define PCI_EXP_DPC_SOURCE_ID 10 /* DPC Source Identifier */
966 967
968/* Precision Time Measurement */
969#define PCI_PTM_CAP 0x04 /* PTM Capability */
970#define PCI_PTM_CAP_REQ 0x00000001 /* Requester capable */
971#define PCI_PTM_CAP_ROOT 0x00000004 /* Root capable */
972#define PCI_PTM_GRANULARITY_MASK 0x0000FF00 /* Clock granularity */
973#define PCI_PTM_CTRL 0x08 /* PTM Control */
974#define PCI_PTM_CTRL_ENABLE 0x00000001 /* PTM enable */
975#define PCI_PTM_CTRL_ROOT 0x00000002 /* Root select */
976
967#endif /* LINUX_PCI_REGS_H */ 977#endif /* LINUX_PCI_REGS_H */