aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/pcie
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci/pcie')
-rw-r--r--drivers/pci/pcie/portdrv.h6
-rw-r--r--drivers/pci/pcie/portdrv_core.c206
2 files changed, 173 insertions, 39 deletions
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h
index ad4d082a0344..5b818bd835ef 100644
--- a/drivers/pci/pcie/portdrv.h
+++ b/drivers/pci/pcie/portdrv.h
@@ -25,6 +25,12 @@
25#define PCIE_CAPABILITIES_REG 0x2 25#define PCIE_CAPABILITIES_REG 0x2
26#define PCIE_SLOT_CAPABILITIES_REG 0x14 26#define PCIE_SLOT_CAPABILITIES_REG 0x14
27#define PCIE_PORT_DEVICE_MAXSERVICES 4 27#define PCIE_PORT_DEVICE_MAXSERVICES 4
28#define PCIE_PORT_MSI_VECTOR_MASK 0x1f
29/*
30 * According to the PCI Express Base Specification 2.0, the indices of the MSI-X
31 * table entires used by port services must not exceed 31
32 */
33#define PCIE_PORT_MAX_MSIX_ENTRIES 32
28 34
29#define get_descriptor_id(type, service) (((type - 4) << 4) | service) 35#define get_descriptor_id(type, service) (((type - 4) << 4) | service)
30 36
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c
index 843d9e30dd3b..3aea92a92928 100644
--- a/drivers/pci/pcie/portdrv_core.c
+++ b/drivers/pci/pcie/portdrv_core.c
@@ -31,6 +31,152 @@ static void release_pcie_device(struct device *dev)
31} 31}
32 32
33/** 33/**
34 * pcie_port_msix_add_entry - add entry to given array of MSI-X entries
35 * @entries: Array of MSI-X entries
36 * @new_entry: Index of the entry to add to the array
37 * @nr_entries: Number of entries aleady in the array
38 *
39 * Return value: Position of the added entry in the array
40 */
41static int pcie_port_msix_add_entry(
42 struct msix_entry *entries, int new_entry, int nr_entries)
43{
44 int j;
45
46 for (j = 0; j < nr_entries; j++)
47 if (entries[j].entry == new_entry)
48 return j;
49
50 entries[j].entry = new_entry;
51 return j;
52}
53
54/**
55 * pcie_port_enable_msix - try to set up MSI-X as interrupt mode for given port
56 * @dev: PCI Express port to handle
57 * @vectors: Array of interrupt vectors to populate
58 * @mask: Bitmask of port capabilities returned by get_port_device_capability()
59 *
60 * Return value: 0 on success, error code on failure
61 */
62static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask)
63{
64 struct msix_entry *msix_entries;
65 int idx[PCIE_PORT_DEVICE_MAXSERVICES];
66 int nr_entries, status, pos, i, nvec;
67 u16 reg16;
68 u32 reg32;
69
70 nr_entries = pci_msix_table_size(dev);
71 if (!nr_entries)
72 return -EINVAL;
73 if (nr_entries > PCIE_PORT_MAX_MSIX_ENTRIES)
74 nr_entries = PCIE_PORT_MAX_MSIX_ENTRIES;
75
76 msix_entries = kzalloc(sizeof(*msix_entries) * nr_entries, GFP_KERNEL);
77 if (!msix_entries)
78 return -ENOMEM;
79
80 /*
81 * Allocate as many entries as the port wants, so that we can check
82 * which of them will be useful. Moreover, if nr_entries is correctly
83 * equal to the number of entries this port actually uses, we'll happily
84 * go through without any tricks.
85 */
86 for (i = 0; i < nr_entries; i++)
87 msix_entries[i].entry = i;
88
89 status = pci_enable_msix(dev, msix_entries, nr_entries);
90 if (status)
91 goto Exit;
92
93 for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
94 idx[i] = -1;
95 status = -EIO;
96 nvec = 0;
97
98 if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP)) {
99 int entry;
100
101 /*
102 * The code below follows the PCI Express Base Specification 2.0
103 * stating in Section 6.1.6 that "PME and Hot-Plug Event
104 * interrupts (when both are implemented) always share the same
105 * MSI or MSI-X vector, as indicated by the Interrupt Message
106 * Number field in the PCI Express Capabilities register", where
107 * according to Section 7.8.2 of the specification "For MSI-X,
108 * the value in this field indicates which MSI-X Table entry is
109 * used to generate the interrupt message."
110 */
111 pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
112 pci_read_config_word(dev, pos + PCIE_CAPABILITIES_REG, &reg16);
113 entry = (reg16 >> 9) & PCIE_PORT_MSI_VECTOR_MASK;
114 if (entry >= nr_entries)
115 goto Error;
116
117 i = pcie_port_msix_add_entry(msix_entries, entry, nvec);
118 if (i == nvec)
119 nvec++;
120
121 idx[PCIE_PORT_SERVICE_PME_SHIFT] = i;
122 idx[PCIE_PORT_SERVICE_HP_SHIFT] = i;
123 }
124
125 if (mask & PCIE_PORT_SERVICE_AER) {
126 int entry;
127
128 /*
129 * The code below follows Section 7.10.10 of the PCI Express
130 * Base Specification 2.0 stating that bits 31-27 of the Root
131 * Error Status Register contain a value indicating which of the
132 * MSI/MSI-X vectors assigned to the port is going to be used
133 * for AER, where "For MSI-X, the value in this register
134 * indicates which MSI-X Table entry is used to generate the
135 * interrupt message."
136 */
137 pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
138 pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &reg32);
139 entry = reg32 >> 27;
140 if (entry >= nr_entries)
141 goto Error;
142
143 i = pcie_port_msix_add_entry(msix_entries, entry, nvec);
144 if (i == nvec)
145 nvec++;
146
147 idx[PCIE_PORT_SERVICE_AER_SHIFT] = i;
148 }
149
150 /*
151 * If nvec is equal to the allocated number of entries, we can just use
152 * what we have. Otherwise, the port has some extra entries not for the
153 * services we know and we need to work around that.
154 */
155 if (nvec == nr_entries) {
156 status = 0;
157 } else {
158 /* Drop the temporary MSI-X setup */
159 pci_disable_msix(dev);
160
161 /* Now allocate the MSI-X vectors for real */
162 status = pci_enable_msix(dev, msix_entries, nvec);
163 if (status)
164 goto Exit;
165 }
166
167 for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
168 vectors[i] = idx[i] >= 0 ? msix_entries[idx[i]].vector : -1;
169
170 Exit:
171 kfree(msix_entries);
172 return status;
173
174 Error:
175 pci_disable_msix(dev);
176 goto Exit;
177}
178
179/**
34 * assign_interrupt_mode - choose interrupt mode for PCI Express port services 180 * assign_interrupt_mode - choose interrupt mode for PCI Express port services
35 * (INTx, MSI-X, MSI) and set up vectors 181 * (INTx, MSI-X, MSI) and set up vectors
36 * @dev: PCI Express port to handle 182 * @dev: PCI Express port to handle
@@ -42,49 +188,31 @@ static void release_pcie_device(struct device *dev)
42static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask) 188static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask)
43{ 189{
44 struct pcie_port_data *port_data = pci_get_drvdata(dev); 190 struct pcie_port_data *port_data = pci_get_drvdata(dev);
45 int i, pos, nvec, status = -EINVAL; 191 int irq, interrupt_mode = PCIE_PORT_NO_IRQ;
46 int interrupt_mode = PCIE_PORT_NO_IRQ; 192 int i;
47
48 /* Set INTx as default */
49 for (i = 0, nvec = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
50 if (mask & (1 << i))
51 nvec++;
52 vectors[i] = dev->irq;
53 }
54 if (dev->pin)
55 interrupt_mode = PCIE_PORT_INTx_MODE;
56 193
57 /* Check MSI quirk */ 194 /* Check MSI quirk */
58 if (port_data->port_type == PCIE_RC_PORT && pcie_mch_quirk) 195 if (port_data->port_type == PCIE_RC_PORT && pcie_mch_quirk)
59 return interrupt_mode; 196 goto Fallback;
197
198 /* Try to use MSI-X if supported */
199 if (!pcie_port_enable_msix(dev, vectors, mask))
200 return PCIE_PORT_MSIX_MODE;
201
202 /* We're not going to use MSI-X, so try MSI and fall back to INTx */
203 if (!pci_enable_msi(dev))
204 interrupt_mode = PCIE_PORT_MSI_MODE;
205
206 Fallback:
207 if (interrupt_mode == PCIE_PORT_NO_IRQ && dev->pin)
208 interrupt_mode = PCIE_PORT_INTx_MODE;
209
210 irq = interrupt_mode != PCIE_PORT_NO_IRQ ? dev->irq : -1;
211 for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
212 vectors[i] = irq;
213
214 vectors[PCIE_PORT_SERVICE_VC_SHIFT] = -1;
60 215
61 /* Select MSI-X over MSI if supported */
62 pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
63 if (pos) {
64 struct msix_entry msix_entries[PCIE_PORT_DEVICE_MAXSERVICES] =
65 {{0, 0}, {0, 1}, {0, 2}, {0, 3}};
66 status = pci_enable_msix(dev, msix_entries, nvec);
67 if (!status) {
68 int j = 0;
69
70 interrupt_mode = PCIE_PORT_MSIX_MODE;
71 for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
72 if (mask & (1 << i))
73 vectors[i] = msix_entries[j++].vector;
74 }
75 }
76 }
77 if (status) {
78 pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
79 if (pos) {
80 status = pci_enable_msi(dev);
81 if (!status) {
82 interrupt_mode = PCIE_PORT_MSI_MODE;
83 for (i = 0;i < PCIE_PORT_DEVICE_MAXSERVICES;i++)
84 vectors[i] = dev->irq;
85 }
86 }
87 }
88 return interrupt_mode; 216 return interrupt_mode;
89} 217}
90 218