diff options
-rw-r--r-- | drivers/pci/pci.h | 6 | ||||
-rw-r--r-- | drivers/pci/pcie/Kconfig | 11 | ||||
-rw-r--r-- | drivers/pci/pcie/Makefile | 1 | ||||
-rw-r--r-- | drivers/pci/pcie/ptm.c | 142 | ||||
-rw-r--r-- | drivers/pci/probe.c | 3 | ||||
-rw-r--r-- | include/linux/pci.h | 13 | ||||
-rw-r--r-- | include/uapi/linux/pci_regs.h | 12 |
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 | ||
333 | void pci_enable_acs(struct pci_dev *dev); | 333 | void pci_enable_acs(struct pci_dev *dev); |
334 | 334 | ||
335 | #ifdef CONFIG_PCIE_PTM | ||
336 | void pci_ptm_init(struct pci_dev *dev); | ||
337 | #else | ||
338 | static inline void pci_ptm_init(struct pci_dev *dev) { } | ||
339 | #endif | ||
340 | |||
335 | struct pci_dev_reset_methods { | 341 | struct 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 | |||
96 | config 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/ | |||
16 | obj-$(CONFIG_PCIE_PME) += pme.o | 16 | obj-$(CONFIG_PCIE_PME) += pme.o |
17 | 17 | ||
18 | obj-$(CONFIG_PCIE_DPC) += pcie-dpc.o | 18 | obj-$(CONFIG_PCIE_DPC) += pcie-dpc.o |
19 | obj-$(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 | |||
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); | ||
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) { } | |||
1402 | static inline int pci_ats_queue_depth(struct pci_dev *d) { return -ENODEV; } | 1408 | static inline int pci_ats_queue_depth(struct pci_dev *d) { return -ENODEV; } |
1403 | #endif | 1409 | #endif |
1404 | 1410 | ||
1411 | #ifdef CONFIG_PCIE_PTM | ||
1412 | int pci_enable_ptm(struct pci_dev *dev, u8 *granularity); | ||
1413 | #else | ||
1414 | static inline int pci_enable_ptm(struct pci_dev *dev, u8 *granularity) | ||
1415 | { return -EINVAL; } | ||
1416 | #endif | ||
1417 | |||
1405 | void pci_cfg_access_lock(struct pci_dev *dev); | 1418 | void pci_cfg_access_lock(struct pci_dev *dev); |
1406 | bool pci_cfg_access_trylock(struct pci_dev *dev); | 1419 | bool pci_cfg_access_trylock(struct pci_dev *dev); |
1407 | void pci_cfg_access_unlock(struct pci_dev *dev); | 1420 | void 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 */ |