diff options
| author | Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com> | 2010-04-15 00:21:27 -0400 |
|---|---|---|
| committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2010-05-11 15:01:38 -0400 |
| commit | 89713422a768458a0d375f0c2f3586cd5ccde6a1 (patch) | |
| tree | c446440123602cdb5320617ac7a8c2dbf514ff41 /drivers | |
| parent | 517cae3829ae8cc3033c24f60e64eb251b2f0d14 (diff) | |
PCI: aerdrv: introduce default_downstream_reset_link
I noticed that when I inject a fatal error to an endpoint via
aer-inject, aer_root_reset() is called as reset_link for a
downstream port at upstream of the endpoint:
pcieport 0000:00:06.0: AER: Uncorrected (Fatal) error received: id=5401
:
pcieport 0000:52:02.0: Root Port link has been reset
It externally appears to be working, but internally issues some
accesses to PCI_ERR_ROOT_COMMAND/STATUS registers that is for
root port so not available on downstream port.
This patch introduces default_downstream_reset_link that is
a version of aer_root_reset() with no accesses to root port's
register. It is used for downstream ports that has no reset_link
function its specific.
This patch also updates related description in pcieaer-howto.txt.
Some minor fixes are included.
Signed-off-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
Reviewed-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/pci/pcie/aer/aerdrv.c | 23 | ||||
| -rw-r--r-- | drivers/pci/pcie/aer/aerdrv.h | 1 | ||||
| -rw-r--r-- | drivers/pci/pcie/aer/aerdrv_core.c | 78 |
3 files changed, 63 insertions, 39 deletions
diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index cbc7cc77b2c3..a225d58c1ac8 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c | |||
| @@ -341,7 +341,6 @@ static int __devinit aer_probe(struct pcie_device *dev) | |||
| 341 | **/ | 341 | **/ |
| 342 | static pci_ers_result_t aer_root_reset(struct pci_dev *dev) | 342 | static pci_ers_result_t aer_root_reset(struct pci_dev *dev) |
| 343 | { | 343 | { |
| 344 | u16 p2p_ctrl; | ||
| 345 | u32 reg32; | 344 | u32 reg32; |
| 346 | int pos; | 345 | int pos; |
| 347 | 346 | ||
| @@ -352,27 +351,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev) | |||
| 352 | reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK; | 351 | reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK; |
| 353 | pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32); | 352 | pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32); |
| 354 | 353 | ||
| 355 | /* Assert Secondary Bus Reset */ | 354 | aer_do_secondary_bus_reset(dev); |
| 356 | pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &p2p_ctrl); | ||
| 357 | p2p_ctrl |= PCI_BRIDGE_CTL_BUS_RESET; | ||
| 358 | pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl); | ||
| 359 | |||
| 360 | /* | ||
| 361 | * we should send hot reset message for 2ms to allow it time to | ||
| 362 | * propogate to all downstream ports | ||
| 363 | */ | ||
| 364 | msleep(2); | ||
| 365 | |||
| 366 | /* De-assert Secondary Bus Reset */ | ||
| 367 | p2p_ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET; | ||
| 368 | pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl); | ||
| 369 | |||
| 370 | /* | ||
| 371 | * System software must wait for at least 100ms from the end | ||
| 372 | * of a reset of one or more device before it is permitted | ||
| 373 | * to issue Configuration Requests to those devices. | ||
| 374 | */ | ||
| 375 | msleep(200); | ||
| 376 | dev_printk(KERN_DEBUG, &dev->dev, "Root Port link has been reset\n"); | 355 | dev_printk(KERN_DEBUG, &dev->dev, "Root Port link has been reset\n"); |
| 377 | 356 | ||
| 378 | /* Clear Root Error Status */ | 357 | /* Clear Root Error Status */ |
diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index d0f8291c5ca0..7aaae2d2bd67 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h | |||
| @@ -114,6 +114,7 @@ static inline pci_ers_result_t merge_result(enum pci_ers_result orig, | |||
| 114 | } | 114 | } |
| 115 | 115 | ||
| 116 | extern struct bus_type pcie_port_bus_type; | 116 | extern struct bus_type pcie_port_bus_type; |
| 117 | extern void aer_do_secondary_bus_reset(struct pci_dev *dev); | ||
| 117 | extern int aer_init(struct pcie_device *dev); | 118 | extern int aer_init(struct pcie_device *dev); |
| 118 | extern void aer_isr(struct work_struct *work); | 119 | extern void aer_isr(struct work_struct *work); |
| 119 | extern void aer_print_error(struct pci_dev *dev, struct aer_err_info *info); | 120 | extern void aer_print_error(struct pci_dev *dev, struct aer_err_info *info); |
diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 8fb14aeb74dd..ce42cac99dd3 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c | |||
| @@ -373,6 +373,53 @@ static pci_ers_result_t broadcast_error_message(struct pci_dev *dev, | |||
| 373 | return result_data.result; | 373 | return result_data.result; |
| 374 | } | 374 | } |
| 375 | 375 | ||
| 376 | /** | ||
| 377 | * aer_do_secondary_bus_reset - perform secondary bus reset | ||
| 378 | * @dev: pointer to bridge's pci_dev data structure | ||
| 379 | * | ||
| 380 | * Invoked when performing link reset at Root Port or Downstream Port. | ||
| 381 | */ | ||
| 382 | void aer_do_secondary_bus_reset(struct pci_dev *dev) | ||
| 383 | { | ||
| 384 | u16 p2p_ctrl; | ||
| 385 | |||
| 386 | /* Assert Secondary Bus Reset */ | ||
| 387 | pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &p2p_ctrl); | ||
| 388 | p2p_ctrl |= PCI_BRIDGE_CTL_BUS_RESET; | ||
| 389 | pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl); | ||
| 390 | |||
| 391 | /* | ||
| 392 | * we should send hot reset message for 2ms to allow it time to | ||
| 393 | * propagate to all downstream ports | ||
| 394 | */ | ||
| 395 | msleep(2); | ||
| 396 | |||
| 397 | /* De-assert Secondary Bus Reset */ | ||
| 398 | p2p_ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET; | ||
| 399 | pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl); | ||
| 400 | |||
| 401 | /* | ||
| 402 | * System software must wait for at least 100ms from the end | ||
| 403 | * of a reset of one or more device before it is permitted | ||
| 404 | * to issue Configuration Requests to those devices. | ||
| 405 | */ | ||
| 406 | msleep(200); | ||
| 407 | } | ||
| 408 | |||
| 409 | /** | ||
| 410 | * default_downstream_reset_link - default reset function for Downstream Port | ||
| 411 | * @dev: pointer to downstream port's pci_dev data structure | ||
| 412 | * | ||
| 413 | * Invoked when performing link reset at Downstream Port w/ no aer driver. | ||
| 414 | */ | ||
| 415 | static pci_ers_result_t default_downstream_reset_link(struct pci_dev *dev) | ||
| 416 | { | ||
| 417 | aer_do_secondary_bus_reset(dev); | ||
| 418 | dev_printk(KERN_DEBUG, &dev->dev, | ||
| 419 | "Downstream Port link has been reset\n"); | ||
| 420 | return PCI_ERS_RESULT_RECOVERED; | ||
| 421 | } | ||
| 422 | |||
| 376 | static int find_aer_service_iter(struct device *device, void *data) | 423 | static int find_aer_service_iter(struct device *device, void *data) |
| 377 | { | 424 | { |
| 378 | struct pcie_port_service_driver *service_driver, **drv; | 425 | struct pcie_port_service_driver *service_driver, **drv; |
| @@ -406,31 +453,28 @@ static pci_ers_result_t reset_link(struct pcie_device *aerdev, | |||
| 406 | pci_ers_result_t status; | 453 | pci_ers_result_t status; |
| 407 | struct pcie_port_service_driver *driver; | 454 | struct pcie_port_service_driver *driver; |
| 408 | 455 | ||
| 409 | if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) | 456 | if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) { |
| 457 | /* Reset this port for all subordinates */ | ||
| 410 | udev = dev; | 458 | udev = dev; |
| 411 | else | 459 | } else { |
| 460 | /* Reset the upstream component (likely downstream port) */ | ||
| 412 | udev = dev->bus->self; | 461 | udev = dev->bus->self; |
| 462 | } | ||
| 413 | 463 | ||
| 414 | /* Use the aer driver of the component firstly */ | 464 | /* Use the aer driver of the component firstly */ |
| 415 | driver = find_aer_service(udev); | 465 | driver = find_aer_service(udev); |
| 416 | 466 | ||
| 417 | /* | 467 | if (driver && driver->reset_link) { |
| 418 | * If it hasn't the driver and is downstream port, use the root port's | 468 | status = driver->reset_link(udev); |
| 419 | */ | 469 | } else if (udev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM) { |
| 420 | if (!driver || !driver->reset_link) { | 470 | status = default_downstream_reset_link(udev); |
| 421 | if (udev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM && | 471 | } else { |
| 422 | aerdev->device.driver && | 472 | dev_printk(KERN_DEBUG, &dev->dev, |
| 423 | to_service_driver(aerdev->device.driver)->reset_link) { | 473 | "no link-reset support at upstream device %s\n", |
| 424 | driver = to_service_driver(aerdev->device.driver); | 474 | pci_name(udev)); |
| 425 | } else { | 475 | return PCI_ERS_RESULT_DISCONNECT; |
| 426 | dev_printk(KERN_DEBUG, &dev->dev, | ||
| 427 | "no link-reset support at upstream device %s\n", | ||
| 428 | pci_name(udev)); | ||
| 429 | return PCI_ERS_RESULT_DISCONNECT; | ||
| 430 | } | ||
| 431 | } | 476 | } |
| 432 | 477 | ||
| 433 | status = driver->reset_link(udev); | ||
| 434 | if (status != PCI_ERS_RESULT_RECOVERED) { | 478 | if (status != PCI_ERS_RESULT_RECOVERED) { |
| 435 | dev_printk(KERN_DEBUG, &dev->dev, | 479 | dev_printk(KERN_DEBUG, &dev->dev, |
| 436 | "link reset at upstream device %s failed\n", | 480 | "link reset at upstream device %s failed\n", |
