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", |