diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2017-11-14 13:11:23 -0500 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2017-11-14 13:11:23 -0500 |
commit | 6018182d3158505f11103adaee8ffb53424df986 (patch) | |
tree | bf2421e0bf69b3a3b74d26acd706bdf264e3ada4 | |
parent | 65a129d7847e4f5b24c6e56ab49090b1f6d0f3dc (diff) | |
parent | a579ba49a9e27bfc1d5cb69b0ea3781d8df46b5b (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.c | 171 | ||||
-rw-r--r-- | include/uapi/linux/pci_regs.h | 2 |
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 | */ |
55 | static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask) | 51 | static 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, ®16); | 65 | pcie_capability_read_word(dev, PCI_EXP_FLAGS, ®16); |
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, ®32); | 72 | if (pos) { |
117 | entry = reg32 >> 27; | 73 | pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, |
118 | if (entry >= nr_entries) | 74 | ®32); |
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, ®16); | 82 | if (pos) { |
144 | entry = reg16 & 0x1f; | 83 | pci_read_config_word(dev, pos + PCI_EXP_DPC_CAP, |
145 | if (entry >= nr_entries) | 84 | ®16); |
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 | */ | ||
102 | static 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 | ||
171 | out_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 */ |