diff options
author | Matthew Wilcox <matthew@wil.cx> | 2009-12-13 08:11:33 -0500 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2010-02-22 19:15:18 -0500 |
commit | 9be60ca0497a2563662fde4c9007841c3b79a742 (patch) | |
tree | d31bc38997f7591d818d478ba91a7f07dee9bede | |
parent | 3749c51ac6c1560aa1cb1520066bed84c6f8152a (diff) |
PCI: Add support for detection of PCIe and PCI-X bus speeds
Both PCIe and PCI-X bridges report their secondary bus speed in their
respective capabilities.
Signed-off-by: Matthew Wilcox <willy@linux.intel.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
-rw-r--r-- | drivers/pci/probe.c | 66 |
1 files changed, 66 insertions, 0 deletions
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 51cf8982fec7..188ee9cf0605 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c | |||
@@ -393,6 +393,25 @@ static struct pci_bus * pci_alloc_bus(void) | |||
393 | return b; | 393 | return b; |
394 | } | 394 | } |
395 | 395 | ||
396 | static unsigned char pcix_bus_speed[] = { | ||
397 | PCI_SPEED_UNKNOWN, /* 0 */ | ||
398 | PCI_SPEED_66MHz_PCIX, /* 1 */ | ||
399 | PCI_SPEED_100MHz_PCIX, /* 2 */ | ||
400 | PCI_SPEED_133MHz_PCIX, /* 3 */ | ||
401 | PCI_SPEED_UNKNOWN, /* 4 */ | ||
402 | PCI_SPEED_66MHz_PCIX_ECC, /* 5 */ | ||
403 | PCI_SPEED_100MHz_PCIX_ECC, /* 6 */ | ||
404 | PCI_SPEED_133MHz_PCIX_ECC, /* 7 */ | ||
405 | PCI_SPEED_UNKNOWN, /* 8 */ | ||
406 | PCI_SPEED_66MHz_PCIX_266, /* 9 */ | ||
407 | PCI_SPEED_100MHz_PCIX_266, /* A */ | ||
408 | PCI_SPEED_133MHz_PCIX_266, /* B */ | ||
409 | PCI_SPEED_UNKNOWN, /* C */ | ||
410 | PCI_SPEED_66MHz_PCIX_533, /* D */ | ||
411 | PCI_SPEED_100MHz_PCIX_533, /* E */ | ||
412 | PCI_SPEED_133MHz_PCIX_533 /* F */ | ||
413 | }; | ||
414 | |||
396 | static unsigned char pcie_link_speed[] = { | 415 | static unsigned char pcie_link_speed[] = { |
397 | PCI_SPEED_UNKNOWN, /* 0 */ | 416 | PCI_SPEED_UNKNOWN, /* 0 */ |
398 | PCIE_SPEED_2_5GT, /* 1 */ | 417 | PCIE_SPEED_2_5GT, /* 1 */ |
@@ -418,6 +437,51 @@ void pcie_update_link_speed(struct pci_bus *bus, u16 linksta) | |||
418 | } | 437 | } |
419 | EXPORT_SYMBOL_GPL(pcie_update_link_speed); | 438 | EXPORT_SYMBOL_GPL(pcie_update_link_speed); |
420 | 439 | ||
440 | static void pci_set_bus_speed(struct pci_bus *bus) | ||
441 | { | ||
442 | struct pci_dev *bridge = bus->self; | ||
443 | int pos; | ||
444 | |||
445 | pos = pci_find_capability(bridge, PCI_CAP_ID_PCIX); | ||
446 | if (pos) { | ||
447 | u16 status; | ||
448 | enum pci_bus_speed max; | ||
449 | pci_read_config_word(bridge, pos + 2, &status); | ||
450 | |||
451 | if (status & 0x8000) { | ||
452 | max = PCI_SPEED_133MHz_PCIX_533; | ||
453 | } else if (status & 0x4000) { | ||
454 | max = PCI_SPEED_133MHz_PCIX_266; | ||
455 | } else if (status & 0x0002) { | ||
456 | if (((status >> 12) & 0x3) == 2) { | ||
457 | max = PCI_SPEED_133MHz_PCIX_ECC; | ||
458 | } else { | ||
459 | max = PCI_SPEED_133MHz_PCIX; | ||
460 | } | ||
461 | } else { | ||
462 | max = PCI_SPEED_66MHz_PCIX; | ||
463 | } | ||
464 | |||
465 | bus->max_bus_speed = max; | ||
466 | bus->cur_bus_speed = pcix_bus_speed[(status >> 6) & 0xf]; | ||
467 | |||
468 | return; | ||
469 | } | ||
470 | |||
471 | pos = pci_find_capability(bridge, PCI_CAP_ID_EXP); | ||
472 | if (pos) { | ||
473 | u32 linkcap; | ||
474 | u16 linksta; | ||
475 | |||
476 | pci_read_config_dword(bridge, pos + PCI_EXP_LNKCAP, &linkcap); | ||
477 | bus->max_bus_speed = pcie_link_speed[linkcap & 0xf]; | ||
478 | |||
479 | pci_read_config_word(bridge, pos + PCI_EXP_LNKSTA, &linksta); | ||
480 | pcie_update_link_speed(bus, linksta); | ||
481 | } | ||
482 | } | ||
483 | |||
484 | |||
421 | static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, | 485 | static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, |
422 | struct pci_dev *bridge, int busnr) | 486 | struct pci_dev *bridge, int busnr) |
423 | { | 487 | { |
@@ -457,6 +521,8 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, | |||
457 | child->self = bridge; | 521 | child->self = bridge; |
458 | child->bridge = get_device(&bridge->dev); | 522 | child->bridge = get_device(&bridge->dev); |
459 | 523 | ||
524 | pci_set_bus_speed(child); | ||
525 | |||
460 | /* Set up default resource pointers and names.. */ | 526 | /* Set up default resource pointers and names.. */ |
461 | for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) { | 527 | for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) { |
462 | child->resource[i] = &bridge->resource[PCI_BRIDGE_RESOURCES+i]; | 528 | child->resource[i] = &bridge->resource[PCI_BRIDGE_RESOURCES+i]; |