diff options
-rw-r--r-- | drivers/pci/pcie/portdrv.h | 5 | ||||
-rw-r--r-- | drivers/pci/pcie/portdrv_core.c | 95 | ||||
-rw-r--r-- | include/linux/pcieport_if.h | 1 |
3 files changed, 39 insertions, 62 deletions
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index 2529f3f2ea5a..b0dcbc73415e 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h | |||
@@ -28,8 +28,9 @@ | |||
28 | 28 | ||
29 | #define get_descriptor_id(type, service) (((type - 4) << 4) | service) | 29 | #define get_descriptor_id(type, service) (((type - 4) << 4) | service) |
30 | 30 | ||
31 | struct pcie_port_device_ext { | 31 | struct pcie_port_data { |
32 | int interrupt_mode; /* [0:INTx | 1:MSI | 2:MSI-X] */ | 32 | int port_type; /* Type of the port */ |
33 | int port_irq_mode; /* [0:INTx | 1:MSI | 2:MSI-X] */ | ||
33 | }; | 34 | }; |
34 | 35 | ||
35 | extern struct bus_type pcie_port_bus_type; | 36 | extern struct bus_type pcie_port_bus_type; |
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 8b3f8c18032f..273e97619bce 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c | |||
@@ -15,10 +15,9 @@ | |||
15 | #include <linux/slab.h> | 15 | #include <linux/slab.h> |
16 | #include <linux/pcieport_if.h> | 16 | #include <linux/pcieport_if.h> |
17 | 17 | ||
18 | #include "../pci.h" | ||
18 | #include "portdrv.h" | 19 | #include "portdrv.h" |
19 | 20 | ||
20 | extern int pcie_mch_quirk; /* MSI-quirk Indicator */ | ||
21 | |||
22 | /** | 21 | /** |
23 | * release_pcie_device - free PCI Express port service device structure | 22 | * release_pcie_device - free PCI Express port service device structure |
24 | * @dev: Port service device to release | 23 | * @dev: Port service device to release |
@@ -31,28 +30,6 @@ static void release_pcie_device(struct device *dev) | |||
31 | kfree(to_pcie_device(dev)); | 30 | kfree(to_pcie_device(dev)); |
32 | } | 31 | } |
33 | 32 | ||
34 | static int is_msi_quirked(struct pci_dev *dev) | ||
35 | { | ||
36 | int port_type, quirk = 0; | ||
37 | u16 reg16; | ||
38 | |||
39 | pci_read_config_word(dev, | ||
40 | pci_find_capability(dev, PCI_CAP_ID_EXP) + | ||
41 | PCIE_CAPABILITIES_REG, ®16); | ||
42 | port_type = (reg16 >> 4) & PORT_TYPE_MASK; | ||
43 | switch(port_type) { | ||
44 | case PCIE_RC_PORT: | ||
45 | if (pcie_mch_quirk == 1) | ||
46 | quirk = 1; | ||
47 | break; | ||
48 | case PCIE_SW_UPSTREAM_PORT: | ||
49 | case PCIE_SW_DOWNSTREAM_PORT: | ||
50 | default: | ||
51 | break; | ||
52 | } | ||
53 | return quirk; | ||
54 | } | ||
55 | |||
56 | /** | 33 | /** |
57 | * assign_interrupt_mode - choose interrupt mode for PCI Express port services | 34 | * assign_interrupt_mode - choose interrupt mode for PCI Express port services |
58 | * (INTx, MSI-X, MSI) and set up vectors | 35 | * (INTx, MSI-X, MSI) and set up vectors |
@@ -64,6 +41,7 @@ static int is_msi_quirked(struct pci_dev *dev) | |||
64 | */ | 41 | */ |
65 | static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask) | 42 | static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask) |
66 | { | 43 | { |
44 | struct pcie_port_data *port_data = pci_get_drvdata(dev); | ||
67 | int i, pos, nvec, status = -EINVAL; | 45 | int i, pos, nvec, status = -EINVAL; |
68 | int interrupt_mode = PCIE_PORT_INTx_MODE; | 46 | int interrupt_mode = PCIE_PORT_INTx_MODE; |
69 | 47 | ||
@@ -75,7 +53,7 @@ static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask) | |||
75 | } | 53 | } |
76 | 54 | ||
77 | /* Check MSI quirk */ | 55 | /* Check MSI quirk */ |
78 | if (is_msi_quirked(dev)) | 56 | if (port_data->port_type == PCIE_RC_PORT && pcie_mch_quirk) |
79 | return interrupt_mode; | 57 | return interrupt_mode; |
80 | 58 | ||
81 | /* Select MSI-X over MSI if supported */ | 59 | /* Select MSI-X over MSI if supported */ |
@@ -132,13 +110,11 @@ static int get_port_device_capability(struct pci_dev *dev) | |||
132 | pos + PCIE_SLOT_CAPABILITIES_REG, ®32); | 110 | pos + PCIE_SLOT_CAPABILITIES_REG, ®32); |
133 | if (reg32 & SLOT_HP_CAPABLE_MASK) | 111 | if (reg32 & SLOT_HP_CAPABLE_MASK) |
134 | services |= PCIE_PORT_SERVICE_HP; | 112 | services |= PCIE_PORT_SERVICE_HP; |
135 | } | 113 | } |
136 | /* PME Capable - root port capability */ | 114 | /* AER capable */ |
137 | if (((reg16 >> 4) & PORT_TYPE_MASK) == PCIE_RC_PORT) | ||
138 | services |= PCIE_PORT_SERVICE_PME; | ||
139 | |||
140 | if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR)) | 115 | if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR)) |
141 | services |= PCIE_PORT_SERVICE_AER; | 116 | services |= PCIE_PORT_SERVICE_AER; |
117 | /* VC support */ | ||
142 | if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_VC)) | 118 | if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_VC)) |
143 | services |= PCIE_PORT_SERVICE_VC; | 119 | services |= PCIE_PORT_SERVICE_VC; |
144 | 120 | ||
@@ -152,15 +128,15 @@ static int get_port_device_capability(struct pci_dev *dev) | |||
152 | * @port_type: Type of the port | 128 | * @port_type: Type of the port |
153 | * @service_type: Type of service to associate with the service device | 129 | * @service_type: Type of service to associate with the service device |
154 | * @irq: Interrupt vector to associate with the service device | 130 | * @irq: Interrupt vector to associate with the service device |
155 | * @irq_mode: Interrupt mode of the service (INTx, MSI-X, MSI) | ||
156 | */ | 131 | */ |
157 | static void pcie_device_init(struct pci_dev *parent, struct pcie_device *dev, | 132 | static void pcie_device_init(struct pci_dev *parent, struct pcie_device *dev, |
158 | int port_type, int service_type, int irq, int irq_mode) | 133 | int service_type, int irq) |
159 | { | 134 | { |
135 | struct pcie_port_data *port_data = pci_get_drvdata(parent); | ||
160 | struct device *device; | 136 | struct device *device; |
137 | int port_type = port_data->port_type; | ||
161 | 138 | ||
162 | dev->port = parent; | 139 | dev->port = parent; |
163 | dev->interrupt_mode = irq_mode; | ||
164 | dev->irq = irq; | 140 | dev->irq = irq; |
165 | dev->id.vendor = parent->vendor; | 141 | dev->id.vendor = parent->vendor; |
166 | dev->id.device = parent->device; | 142 | dev->id.device = parent->device; |
@@ -185,10 +161,9 @@ static void pcie_device_init(struct pci_dev *parent, struct pcie_device *dev, | |||
185 | * @port_type: Type of the port | 161 | * @port_type: Type of the port |
186 | * @service_type: Type of service to associate with the service device | 162 | * @service_type: Type of service to associate with the service device |
187 | * @irq: Interrupt vector to associate with the service device | 163 | * @irq: Interrupt vector to associate with the service device |
188 | * @irq_mode: Interrupt mode of the service (INTx, MSI-X, MSI) | ||
189 | */ | 164 | */ |
190 | static struct pcie_device* alloc_pcie_device(struct pci_dev *parent, | 165 | static struct pcie_device* alloc_pcie_device(struct pci_dev *parent, |
191 | int port_type, int service_type, int irq, int irq_mode) | 166 | int service_type, int irq) |
192 | { | 167 | { |
193 | struct pcie_device *device; | 168 | struct pcie_device *device; |
194 | 169 | ||
@@ -196,7 +171,7 @@ static struct pcie_device* alloc_pcie_device(struct pci_dev *parent, | |||
196 | if (!device) | 171 | if (!device) |
197 | return NULL; | 172 | return NULL; |
198 | 173 | ||
199 | pcie_device_init(parent, device, port_type, service_type, irq,irq_mode); | 174 | pcie_device_init(parent, device, service_type, irq); |
200 | return device; | 175 | return device; |
201 | } | 176 | } |
202 | 177 | ||
@@ -230,39 +205,36 @@ int pcie_port_device_probe(struct pci_dev *dev) | |||
230 | */ | 205 | */ |
231 | int pcie_port_device_register(struct pci_dev *dev) | 206 | int pcie_port_device_register(struct pci_dev *dev) |
232 | { | 207 | { |
233 | struct pcie_port_device_ext *p_ext; | 208 | struct pcie_port_data *port_data; |
234 | int status, type, capabilities, irq_mode, i; | 209 | int status, capabilities, irq_mode, i; |
235 | int vectors[PCIE_PORT_DEVICE_MAXSERVICES]; | 210 | int vectors[PCIE_PORT_DEVICE_MAXSERVICES]; |
236 | u16 reg16; | 211 | u16 reg16; |
237 | 212 | ||
238 | /* Allocate port device extension */ | 213 | port_data = kzalloc(sizeof(*port_data), GFP_KERNEL); |
239 | if (!(p_ext = kmalloc(sizeof(struct pcie_port_device_ext), GFP_KERNEL))) | 214 | if (!port_data) |
240 | return -ENOMEM; | 215 | return -ENOMEM; |
241 | 216 | pci_set_drvdata(dev, port_data); | |
242 | pci_set_drvdata(dev, p_ext); | ||
243 | 217 | ||
244 | /* Get port type */ | 218 | /* Get port type */ |
245 | pci_read_config_word(dev, | 219 | pci_read_config_word(dev, |
246 | pci_find_capability(dev, PCI_CAP_ID_EXP) + | 220 | pci_find_capability(dev, PCI_CAP_ID_EXP) + |
247 | PCIE_CAPABILITIES_REG, ®16); | 221 | PCIE_CAPABILITIES_REG, ®16); |
248 | type = (reg16 >> 4) & PORT_TYPE_MASK; | 222 | port_data->port_type = (reg16 >> 4) & PORT_TYPE_MASK; |
249 | 223 | ||
250 | /* Now get port services */ | ||
251 | capabilities = get_port_device_capability(dev); | 224 | capabilities = get_port_device_capability(dev); |
225 | /* Root ports are capable of generating PME too */ | ||
226 | if (port_data->port_type == PCIE_RC_PORT) | ||
227 | capabilities |= PCIE_PORT_SERVICE_PME; | ||
228 | |||
252 | irq_mode = assign_interrupt_mode(dev, vectors, capabilities); | 229 | irq_mode = assign_interrupt_mode(dev, vectors, capabilities); |
253 | p_ext->interrupt_mode = irq_mode; | 230 | port_data->port_irq_mode = irq_mode; |
254 | 231 | ||
255 | /* Allocate child services if any */ | 232 | /* Allocate child services if any */ |
256 | for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) { | 233 | for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) { |
257 | struct pcie_device *child; | 234 | struct pcie_device *child; |
258 | 235 | ||
259 | if (capabilities & (1 << i)) { | 236 | if (capabilities & (1 << i)) { |
260 | child = alloc_pcie_device( | 237 | child = alloc_pcie_device(dev, i, vectors[i]); |
261 | dev, /* parent */ | ||
262 | type, /* port type */ | ||
263 | i, /* service type */ | ||
264 | vectors[i], /* irq */ | ||
265 | irq_mode /* interrupt mode */); | ||
266 | if (child) { | 238 | if (child) { |
267 | status = device_register(&child->device); | 239 | status = device_register(&child->device); |
268 | if (status) { | 240 | if (status) { |
@@ -349,25 +321,30 @@ static int remove_iter(struct device *dev, void *data) | |||
349 | */ | 321 | */ |
350 | void pcie_port_device_remove(struct pci_dev *dev) | 322 | void pcie_port_device_remove(struct pci_dev *dev) |
351 | { | 323 | { |
352 | struct device *device; | 324 | struct pcie_port_data *port_data = pci_get_drvdata(dev); |
353 | unsigned long device_addr; | ||
354 | int interrupt_mode = PCIE_PORT_INTx_MODE; | ||
355 | int status; | 325 | int status; |
356 | 326 | ||
357 | do { | 327 | do { |
328 | unsigned long device_addr; | ||
329 | |||
358 | status = device_for_each_child(&dev->dev, &device_addr, remove_iter); | 330 | status = device_for_each_child(&dev->dev, &device_addr, remove_iter); |
359 | if (status) { | 331 | if (status) { |
360 | device = (struct device*)device_addr; | 332 | struct device *device = (struct device*)device_addr; |
361 | interrupt_mode = (to_pcie_device(device))->interrupt_mode; | ||
362 | put_device(device); | 333 | put_device(device); |
363 | device_unregister(device); | 334 | device_unregister(device); |
364 | } | 335 | } |
365 | } while (status); | 336 | } while (status); |
366 | /* Switch to INTx by default if MSI enabled */ | 337 | |
367 | if (interrupt_mode == PCIE_PORT_MSIX_MODE) | 338 | switch (port_data->port_irq_mode) { |
339 | case PCIE_PORT_MSIX_MODE: | ||
368 | pci_disable_msix(dev); | 340 | pci_disable_msix(dev); |
369 | else if (interrupt_mode == PCIE_PORT_MSI_MODE) | 341 | break; |
342 | case PCIE_PORT_MSI_MODE: | ||
370 | pci_disable_msi(dev); | 343 | pci_disable_msi(dev); |
344 | break; | ||
345 | } | ||
346 | |||
347 | kfree(port_data); | ||
371 | } | 348 | } |
372 | 349 | ||
373 | /** | 350 | /** |
diff --git a/include/linux/pcieport_if.h b/include/linux/pcieport_if.h index 6cd91e3f9820..194409af1037 100644 --- a/include/linux/pcieport_if.h +++ b/include/linux/pcieport_if.h | |||
@@ -36,7 +36,6 @@ struct pcie_port_service_id { | |||
36 | 36 | ||
37 | struct pcie_device { | 37 | struct pcie_device { |
38 | int irq; /* Service IRQ/MSI/MSI-X Vector */ | 38 | int irq; /* Service IRQ/MSI/MSI-X Vector */ |
39 | int interrupt_mode; /* [0:INTx | 1:MSI | 2:MSI-X] */ | ||
40 | struct pcie_port_service_id id; /* Service ID */ | 39 | struct pcie_port_service_id id; /* Service ID */ |
41 | struct pci_dev *port; /* Root/Upstream/Downstream Port */ | 40 | struct pci_dev *port; /* Root/Upstream/Downstream Port */ |
42 | void *priv_data; /* Service Private Data */ | 41 | void *priv_data; /* Service Private Data */ |