aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjorn Helgaas <bhelgaas@google.com>2017-11-14 13:11:23 -0500
committerBjorn Helgaas <bhelgaas@google.com>2017-11-14 13:11:23 -0500
commit6018182d3158505f11103adaee8ffb53424df986 (patch)
treebf2421e0bf69b3a3b74d26acd706bdf264e3ada4
parent65a129d7847e4f5b24c6e56ab49090b1f6d0f3dc (diff)
parenta579ba49a9e27bfc1d5cb69b0ea3781d8df46b5b (diff)
Merge branch 'pci/msi' into next
* pci/msi: PCI/portdrv: Compute MSI/MSI-X IRQ vectors after final allocation PCI/portdrv: Factor out Interrupt Message Number lookup PCI/portdrv: Consolidate comments PCI/portdrv: Add #defines for AER and DPC Interrupt Message Number masks
-rw-r--r--drivers/pci/pcie/portdrv_core.c171
-rw-r--r--include/uapi/linux/pci_regs.h2
2 files changed, 77 insertions, 96 deletions
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c
index 313a21df1692..3cd5eb48644a 100644
--- a/drivers/pci/pcie/portdrv_core.c
+++ b/drivers/pci/pcie/portdrv_core.c
@@ -43,134 +43,113 @@ static void release_pcie_device(struct device *dev)
43 kfree(to_pcie_device(dev)); 43 kfree(to_pcie_device(dev));
44} 44}
45 45
46/** 46/*
47 * pcie_port_enable_irq_vec - try to set up MSI-X or MSI as interrupt mode 47 * Fill in *pme, *aer, *dpc with the relevant Interrupt Message Numbers if
48 * for given port 48 * services are enabled in "mask". Return the number of MSI/MSI-X vectors
49 * @dev: PCI Express port to handle 49 * required to accommodate the largest Message Number.
50 * @irqs: Array of interrupt vectors to populate
51 * @mask: Bitmask of port capabilities returned by get_port_device_capability()
52 *
53 * Return value: 0 on success, error code on failure
54 */ 50 */
55static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask) 51static int pcie_message_numbers(struct pci_dev *dev, int mask,
52 u32 *pme, u32 *aer, u32 *dpc)
56{ 53{
57 int nr_entries, entry, nvec = 0; 54 u32 nvec = 0, pos, reg32;
55 u16 reg16;
58 56
59 /* 57 /*
60 * Allocate as many entries as the port wants, so that we can check 58 * The Interrupt Message Number indicates which vector is used, i.e.,
61 * which of them will be useful. Moreover, if nr_entries is correctly 59 * the MSI-X table entry or the MSI offset between the base Message
62 * equal to the number of entries this port actually uses, we'll happily 60 * Data and the generated interrupt message. See PCIe r3.1, sec
63 * go through without any tricks. 61 * 7.8.2, 7.10.10, 7.31.2.
64 */ 62 */
65 nr_entries = pci_alloc_irq_vectors(dev, 1, PCIE_PORT_MAX_MSI_ENTRIES,
66 PCI_IRQ_MSIX | PCI_IRQ_MSI);
67 if (nr_entries < 0)
68 return nr_entries;
69 63
70 if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP)) { 64 if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP)) {
71 u16 reg16;
72
73 /*
74 * Per PCIe r3.1, sec 6.1.6, "PME and Hot-Plug Event
75 * interrupts (when both are implemented) always share the
76 * same MSI or MSI-X vector, as indicated by the Interrupt
77 * Message Number field in the PCI Express Capabilities
78 * register".
79 *
80 * Per sec 7.8.2, "For MSI, the [Interrupt Message Number]
81 * indicates the offset between the base Message Data and
82 * the interrupt message that is generated."
83 *
84 * "For MSI-X, the [Interrupt Message Number] indicates
85 * which MSI-X Table entry is used to generate the
86 * interrupt message."
87 */
88 pcie_capability_read_word(dev, PCI_EXP_FLAGS, &reg16); 65 pcie_capability_read_word(dev, PCI_EXP_FLAGS, &reg16);
89 entry = (reg16 & PCI_EXP_FLAGS_IRQ) >> 9; 66 *pme = (reg16 & PCI_EXP_FLAGS_IRQ) >> 9;
90 if (entry >= nr_entries) 67 nvec = *pme + 1;
91 goto out_free_irqs;
92
93 irqs[PCIE_PORT_SERVICE_PME_SHIFT] = pci_irq_vector(dev, entry);
94 irqs[PCIE_PORT_SERVICE_HP_SHIFT] = pci_irq_vector(dev, entry);
95
96 nvec = max(nvec, entry + 1);
97 } 68 }
98 69
99 if (mask & PCIE_PORT_SERVICE_AER) { 70 if (mask & PCIE_PORT_SERVICE_AER) {
100 u32 reg32, pos;
101
102 /*
103 * Per PCIe r3.1, sec 7.10.10, the Advanced Error Interrupt
104 * Message Number in the Root Error Status register
105 * indicates which MSI/MSI-X vector is used for AER.
106 *
107 * "For MSI, the [Advanced Error Interrupt Message Number]
108 * indicates the offset between the base Message Data and
109 * the interrupt message that is generated."
110 *
111 * "For MSI-X, the [Advanced Error Interrupt Message
112 * Number] indicates which MSI-X Table entry is used to
113 * generate the interrupt message."
114 */
115 pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); 71 pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
116 pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &reg32); 72 if (pos) {
117 entry = reg32 >> 27; 73 pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS,
118 if (entry >= nr_entries) 74 &reg32);
119 goto out_free_irqs; 75 *aer = (reg32 & PCI_ERR_ROOT_AER_IRQ) >> 27;
120 76 nvec = max(nvec, *aer + 1);
121 irqs[PCIE_PORT_SERVICE_AER_SHIFT] = pci_irq_vector(dev, entry); 77 }
122
123 nvec = max(nvec, entry + 1);
124 } 78 }
125 79
126 if (mask & PCIE_PORT_SERVICE_DPC) { 80 if (mask & PCIE_PORT_SERVICE_DPC) {
127 u16 reg16, pos;
128
129 /*
130 * Per PCIe r4.0 (v0.9), sec 7.9.15.2, the DPC Interrupt
131 * Message Number in the DPC Capability register indicates
132 * which MSI/MSI-X vector is used for DPC.
133 *
134 * "For MSI, the [DPC Interrupt Message Number] indicates
135 * the offset between the base Message Data and the
136 * interrupt message that is generated."
137 *
138 * "For MSI-X, the [DPC Interrupt Message Number] indicates
139 * which MSI-X Table entry is used to generate the
140 * interrupt message."
141 */
142 pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC); 81 pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC);
143 pci_read_config_word(dev, pos + PCI_EXP_DPC_CAP, &reg16); 82 if (pos) {
144 entry = reg16 & 0x1f; 83 pci_read_config_word(dev, pos + PCI_EXP_DPC_CAP,
145 if (entry >= nr_entries) 84 &reg16);
146 goto out_free_irqs; 85 *dpc = reg16 & PCI_EXP_DPC_IRQ;
86 nvec = max(nvec, *dpc + 1);
87 }
88 }
89
90 return nvec;
91}
147 92
148 irqs[PCIE_PORT_SERVICE_DPC_SHIFT] = pci_irq_vector(dev, entry); 93/**
94 * pcie_port_enable_irq_vec - try to set up MSI-X or MSI as interrupt mode
95 * for given port
96 * @dev: PCI Express port to handle
97 * @irqs: Array of interrupt vectors to populate
98 * @mask: Bitmask of port capabilities returned by get_port_device_capability()
99 *
100 * Return value: 0 on success, error code on failure
101 */
102static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask)
103{
104 int nr_entries, nvec;
105 u32 pme = 0, aer = 0, dpc = 0;
149 106
150 nvec = max(nvec, entry + 1); 107 /* Allocate the maximum possible number of MSI/MSI-X vectors */
108 nr_entries = pci_alloc_irq_vectors(dev, 1, PCIE_PORT_MAX_MSI_ENTRIES,
109 PCI_IRQ_MSIX | PCI_IRQ_MSI);
110 if (nr_entries < 0)
111 return nr_entries;
112
113 /* See how many and which Interrupt Message Numbers we actually use */
114 nvec = pcie_message_numbers(dev, mask, &pme, &aer, &dpc);
115 if (nvec > nr_entries) {
116 pci_free_irq_vectors(dev);
117 return -EIO;
151 } 118 }
152 119
153 /* 120 /*
154 * If nvec is equal to the allocated number of entries, we can just use 121 * If we allocated more than we need, free them and reallocate fewer.
155 * what we have. Otherwise, the port has some extra entries not for the 122 *
156 * services we know and we need to work around that. 123 * Reallocating may change the specific vectors we get, so
124 * pci_irq_vector() must be done *after* the reallocation.
125 *
126 * If we're using MSI, hardware is *allowed* to change the Interrupt
127 * Message Numbers when we free and reallocate the vectors, but we
128 * assume it won't because we allocate enough vectors for the
129 * biggest Message Number we found.
157 */ 130 */
158 if (nvec != nr_entries) { 131 if (nvec != nr_entries) {
159 /* Drop the temporary MSI-X setup */
160 pci_free_irq_vectors(dev); 132 pci_free_irq_vectors(dev);
161 133
162 /* Now allocate the MSI-X vectors for real */
163 nr_entries = pci_alloc_irq_vectors(dev, nvec, nvec, 134 nr_entries = pci_alloc_irq_vectors(dev, nvec, nvec,
164 PCI_IRQ_MSIX | PCI_IRQ_MSI); 135 PCI_IRQ_MSIX | PCI_IRQ_MSI);
165 if (nr_entries < 0) 136 if (nr_entries < 0)
166 return nr_entries; 137 return nr_entries;
167 } 138 }
168 139
169 return 0; 140 /* PME and hotplug share an MSI/MSI-X vector */
141 if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP)) {
142 irqs[PCIE_PORT_SERVICE_PME_SHIFT] = pci_irq_vector(dev, pme);
143 irqs[PCIE_PORT_SERVICE_HP_SHIFT] = pci_irq_vector(dev, pme);
144 }
170 145
171out_free_irqs: 146 if (mask & PCIE_PORT_SERVICE_AER)
172 pci_free_irq_vectors(dev); 147 irqs[PCIE_PORT_SERVICE_AER_SHIFT] = pci_irq_vector(dev, aer);
173 return -EIO; 148
149 if (mask & PCIE_PORT_SERVICE_DPC)
150 irqs[PCIE_PORT_SERVICE_DPC_SHIFT] = pci_irq_vector(dev, dpc);
151
152 return 0;
174} 153}
175 154
176/** 155/**
diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h
index 85a4014de42e..12241709092b 100644
--- a/include/uapi/linux/pci_regs.h
+++ b/include/uapi/linux/pci_regs.h
@@ -746,6 +746,7 @@
746#define PCI_ERR_ROOT_FIRST_FATAL 0x00000010 /* First UNC is Fatal */ 746#define PCI_ERR_ROOT_FIRST_FATAL 0x00000010 /* First UNC is Fatal */
747#define PCI_ERR_ROOT_NONFATAL_RCV 0x00000020 /* Non-Fatal Received */ 747#define PCI_ERR_ROOT_NONFATAL_RCV 0x00000020 /* Non-Fatal Received */
748#define PCI_ERR_ROOT_FATAL_RCV 0x00000040 /* Fatal Received */ 748#define PCI_ERR_ROOT_FATAL_RCV 0x00000040 /* Fatal Received */
749#define PCI_ERR_ROOT_AER_IRQ 0xf8000000 /* Advanced Error Interrupt Message Number */
749#define PCI_ERR_ROOT_ERR_SRC 52 /* Error Source Identification */ 750#define PCI_ERR_ROOT_ERR_SRC 52 /* Error Source Identification */
750 751
751/* Virtual Channel */ 752/* Virtual Channel */
@@ -960,6 +961,7 @@
960 961
961/* Downstream Port Containment */ 962/* Downstream Port Containment */
962#define PCI_EXP_DPC_CAP 4 /* DPC Capability */ 963#define PCI_EXP_DPC_CAP 4 /* DPC Capability */
964#define PCI_EXP_DPC_IRQ 0x1f /* DPC Interrupt Message Number */
963#define PCI_EXP_DPC_CAP_RP_EXT 0x20 /* Root Port Extensions for DPC */ 965#define PCI_EXP_DPC_CAP_RP_EXT 0x20 /* Root Port Extensions for DPC */
964#define PCI_EXP_DPC_CAP_POISONED_TLP 0x40 /* Poisoned TLP Egress Blocking Supported */ 966#define PCI_EXP_DPC_CAP_POISONED_TLP 0x40 /* Poisoned TLP Egress Blocking Supported */
965#define PCI_EXP_DPC_CAP_SW_TRIGGER 0x80 /* Software Triggering Supported */ 967#define PCI_EXP_DPC_CAP_SW_TRIGGER 0x80 /* Software Triggering Supported */