pciehp: workaround against Bad DLLP during power off

Set Bad DLLP Mask bit in Correctable Error Mask Register during
turning power off the slot.

This is the workaround against Bad DLLP error that sometimes happen
during turning power off on the slot which conforms to PCI Express
1.0a spec. The cause of this error seems that PCI Express 1.0a spec
doesn't have the following consideration that was added to PCI Express
1.1 spec.

"If the port is associated with a hot-pluggable slot (Hot-Plug
Capable bit in the Slot Capabilities register set to 1b), and
Power Controller Control bit in Slot Control register is 1b(Off),
then any transition to DL Inactive must not be considered an
error."

Signed-off-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Kenji Kaneshige and committed by
Greg Kroah-Hartman
f1050a35 8bb7c7af

+45
+45
drivers/pci/hotplug/pciehp_hpc.c
··· 636 return retval; 637 } 638 639 static int hpc_power_off_slot(struct slot * slot) 640 { 641 struct controller *ctrl = slot->ctrl; 642 u16 slot_cmd; 643 u16 cmd_mask; 644 int retval = 0; 645 646 dbg("%s: slot->hp_slot %x\n", __FUNCTION__, slot->hp_slot); 647 648 slot_cmd = POWER_OFF; 649 cmd_mask = PWR_CTRL; ··· 722 * removed from the slot/adapter. 723 */ 724 msleep(1000); 725 726 return retval; 727 }
··· 636 return retval; 637 } 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, &reg); 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, &reg); 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 + 672 static int hpc_power_off_slot(struct slot * slot) 673 { 674 struct controller *ctrl = slot->ctrl; 675 u16 slot_cmd; 676 u16 cmd_mask; 677 int retval = 0; 678 + int changed; 679 680 dbg("%s: slot->hp_slot %x\n", __FUNCTION__, slot->hp_slot); 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 690 slot_cmd = POWER_OFF; 691 cmd_mask = PWR_CTRL; ··· 680 * removed from the slot/adapter. 681 */ 682 msleep(1000); 683 + 684 + if (changed) 685 + pcie_unmask_bad_dllp(ctrl); 686 687 return retval; 688 }