diff options
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/hotplug/pciehp_hpc.c | 45 |
1 files changed, 45 insertions, 0 deletions
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index b2cde04ede1a..6eba9b2cfb90 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c | |||
@@ -636,15 +636,57 @@ static int hpc_power_on_slot(struct slot * slot) | |||
636 | return retval; | 636 | return retval; |
637 | } | 637 | } |
638 | 638 | ||
639 | static inline int pcie_mask_bad_dllp(struct controller *ctrl) | ||
640 | { | ||
641 | struct pci_dev *dev = ctrl->pci_dev; | ||
642 | int pos; | ||
643 | u32 reg; | ||
644 | |||
645 | pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); | ||
646 | if (!pos) | ||
647 | return 0; | ||
648 | pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, ®); | ||
649 | if (reg & PCI_ERR_COR_BAD_DLLP) | ||
650 | return 0; | ||
651 | reg |= PCI_ERR_COR_BAD_DLLP; | ||
652 | pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg); | ||
653 | return 1; | ||
654 | } | ||
655 | |||
656 | static inline void pcie_unmask_bad_dllp(struct controller *ctrl) | ||
657 | { | ||
658 | struct pci_dev *dev = ctrl->pci_dev; | ||
659 | u32 reg; | ||
660 | int pos; | ||
661 | |||
662 | pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); | ||
663 | if (!pos) | ||
664 | return; | ||
665 | pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, ®); | ||
666 | if (!(reg & PCI_ERR_COR_BAD_DLLP)) | ||
667 | return; | ||
668 | reg &= ~PCI_ERR_COR_BAD_DLLP; | ||
669 | pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg); | ||
670 | } | ||
671 | |||
639 | static int hpc_power_off_slot(struct slot * slot) | 672 | static int hpc_power_off_slot(struct slot * slot) |
640 | { | 673 | { |
641 | struct controller *ctrl = slot->ctrl; | 674 | struct controller *ctrl = slot->ctrl; |
642 | u16 slot_cmd; | 675 | u16 slot_cmd; |
643 | u16 cmd_mask; | 676 | u16 cmd_mask; |
644 | int retval = 0; | 677 | int retval = 0; |
678 | int changed; | ||
645 | 679 | ||
646 | dbg("%s: slot->hp_slot %x\n", __FUNCTION__, slot->hp_slot); | 680 | dbg("%s: slot->hp_slot %x\n", __FUNCTION__, slot->hp_slot); |
647 | 681 | ||
682 | /* | ||
683 | * Set Bad DLLP Mask bit in Correctable Error Mask | ||
684 | * Register. This is the workaround against Bad DLLP error | ||
685 | * that sometimes happens during turning power off the slot | ||
686 | * which conforms to PCI Express 1.0a spec. | ||
687 | */ | ||
688 | changed = pcie_mask_bad_dllp(ctrl); | ||
689 | |||
648 | slot_cmd = POWER_OFF; | 690 | slot_cmd = POWER_OFF; |
649 | cmd_mask = PWR_CTRL; | 691 | cmd_mask = PWR_CTRL; |
650 | /* | 692 | /* |
@@ -681,6 +723,9 @@ static int hpc_power_off_slot(struct slot * slot) | |||
681 | */ | 723 | */ |
682 | msleep(1000); | 724 | msleep(1000); |
683 | 725 | ||
726 | if (changed) | ||
727 | pcie_unmask_bad_dllp(ctrl); | ||
728 | |||
684 | return retval; | 729 | return retval; |
685 | } | 730 | } |
686 | 731 | ||