diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2009-01-13 08:38:34 -0500 |
---|---|---|
committer | Jesse Barnes <jbarnes@hobbes.lan> | 2009-03-19 22:29:20 -0400 |
commit | 1bf83e558cb29d163f4bc6decbc3800ecf4db195 (patch) | |
tree | ccbf5bc39973a438f69fbd39317ef49a974c76ad /drivers | |
parent | e496b617b40f2abf6d49803f56aa1344ce1b9177 (diff) |
PCI: PCIe portdrv: Use driver data to simplify code
PCI Express port driver extension, as defined by struct
pcie_port_device_ext in portdrv.h, is allocated and initialized, but
never used (it also is never freed). Extend it to hold the PCI Express
port type as well as the port interrupt mode, change its name and use it
to simplify the code in portdrv_core.c .
Additionally, remove the redundant interrupt_mode member of struct
pcie_device defined in include/linux/pcieport_if.h .
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pci/pcie/portdrv.h | 5 | ||||
-rw-r--r-- | drivers/pci/pcie/portdrv_core.c | 95 |
2 files changed, 39 insertions, 61 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 | /** |