PCI: pciehp: Disable/enable link during slot power off/on
authorYinghai Lu <yinghai@kernel.org>
Fri, 27 Jan 2012 18:55:15 +0000 (10:55 -0800)
committerJesse Barnes <jbarnes@virtuousgeek.org>
Tue, 14 Feb 2012 16:45:02 +0000 (08:45 -0800)
On a system with a repeater on the system board to support gen2 hotplug,
we found that when an ExpressModule is removed from some slots,
/var/log/messages will be full of "card present/not present" warnings.

It turns out the root complex is continually trying to train the link to
the repeater because the repeater has not been reset.

This patch will disable the link at removal time to allow the repeater
to be reset properly.  This also prevents a potential AER message at
removal time.

Also, when testing hotplug on a system under development, we found if we
boot the system without an EM installed, and later hot-add an EM, it
does not work with Linux, but another OS is ok.  The root cause is that
BIOS left link disabled when slot was empty at boot time, and other OS
is modifying the link disable bit in link ctrl during power on/off.

So we should do the same thing to disable/enable link during power off/on.

-v2: check link DLLA bit instead of 100ms waiting.
     Separate link disable/enable functions to another patch.

Signed-off-by: Yinghai Lu <yinghai.lu@oracle.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
drivers/pci/hotplug/pciehp_hpc.c

index db33688af5888c133e010e9d3a5847135d52e30a..a960faec1021e2cdb351509d747e3fddc3fb1af5 100644 (file)
@@ -606,6 +606,10 @@ int pciehp_power_on_slot(struct slot * slot)
        ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
                 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd);
 
+       retval = pciehp_link_enable(ctrl);
+       if (retval)
+               ctrl_err(ctrl, "%s: Can not enable the link!\n", __func__);
+
        return retval;
 }
 
@@ -616,6 +620,14 @@ int pciehp_power_off_slot(struct slot * slot)
        u16 cmd_mask;
        int retval;
 
+       /* Disable the link at first */
+       pciehp_link_disable(ctrl);
+       /* wait the link is down */
+       if (ctrl->link_active_reporting)
+               pcie_wait_link_not_active(ctrl);
+       else
+               msleep(1000);
+
        slot_cmd = POWER_OFF;
        cmd_mask = PCI_EXP_SLTCTL_PCC;
        retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask);