pciehp: Fix wrong slot control register access
Current pciehp implementaion clears hotplug events without waiting for command completion. Because of this, events might not be cleared properly. To prevent this problem, we must use pciehp_write_cmd() to write to Slot Control register. Signed-off-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com> Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
This commit is contained in:
parent
2d32a9aed2
commit
c27fb883df
|
@ -242,13 +242,12 @@ static inline int pcie_wait_cmd(struct controller *ctrl)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pcie_write_cmd - Issue controller command
|
* pcie_write_cmd - Issue controller command
|
||||||
* @slot: slot to which the command is issued
|
* @ctrl: controller to which the command is issued
|
||||||
* @cmd: command value written to slot control register
|
* @cmd: command value written to slot control register
|
||||||
* @mask: bitmask of slot control register to be modified
|
* @mask: bitmask of slot control register to be modified
|
||||||
*/
|
*/
|
||||||
static int pcie_write_cmd(struct slot *slot, u16 cmd, u16 mask)
|
static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask)
|
||||||
{
|
{
|
||||||
struct controller *ctrl = slot->ctrl;
|
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
u16 slot_status;
|
u16 slot_status;
|
||||||
u16 slot_ctrl;
|
u16 slot_ctrl;
|
||||||
|
@ -468,7 +467,7 @@ static int hpc_toggle_emi(struct slot *slot)
|
||||||
cmd_mask = cmd_mask | HP_INTR_ENABLE;
|
cmd_mask = cmd_mask | HP_INTR_ENABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = pcie_write_cmd(slot, slot_cmd, cmd_mask);
|
rc = pcie_write_cmd(slot->ctrl, slot_cmd, cmd_mask);
|
||||||
slot->last_emi_toggle = get_seconds();
|
slot->last_emi_toggle = get_seconds();
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -500,7 +499,7 @@ static int hpc_set_attention_status(struct slot *slot, u8 value)
|
||||||
cmd_mask = cmd_mask | HP_INTR_ENABLE;
|
cmd_mask = cmd_mask | HP_INTR_ENABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = pcie_write_cmd(slot, slot_cmd, cmd_mask);
|
rc = pcie_write_cmd(ctrl, slot_cmd, cmd_mask);
|
||||||
dbg("%s: SLOTCTRL %x write cmd %x\n",
|
dbg("%s: SLOTCTRL %x write cmd %x\n",
|
||||||
__func__, ctrl->cap_base + SLOTCTRL, slot_cmd);
|
__func__, ctrl->cap_base + SLOTCTRL, slot_cmd);
|
||||||
|
|
||||||
|
@ -520,7 +519,7 @@ static void hpc_set_green_led_on(struct slot *slot)
|
||||||
cmd_mask = cmd_mask | HP_INTR_ENABLE;
|
cmd_mask = cmd_mask | HP_INTR_ENABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
pcie_write_cmd(slot, slot_cmd, cmd_mask);
|
pcie_write_cmd(ctrl, slot_cmd, cmd_mask);
|
||||||
|
|
||||||
dbg("%s: SLOTCTRL %x write cmd %x\n",
|
dbg("%s: SLOTCTRL %x write cmd %x\n",
|
||||||
__func__, ctrl->cap_base + SLOTCTRL, slot_cmd);
|
__func__, ctrl->cap_base + SLOTCTRL, slot_cmd);
|
||||||
|
@ -539,7 +538,7 @@ static void hpc_set_green_led_off(struct slot *slot)
|
||||||
cmd_mask = cmd_mask | HP_INTR_ENABLE;
|
cmd_mask = cmd_mask | HP_INTR_ENABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
pcie_write_cmd(slot, slot_cmd, cmd_mask);
|
pcie_write_cmd(ctrl, slot_cmd, cmd_mask);
|
||||||
dbg("%s: SLOTCTRL %x write cmd %x\n",
|
dbg("%s: SLOTCTRL %x write cmd %x\n",
|
||||||
__func__, ctrl->cap_base + SLOTCTRL, slot_cmd);
|
__func__, ctrl->cap_base + SLOTCTRL, slot_cmd);
|
||||||
}
|
}
|
||||||
|
@ -557,7 +556,7 @@ static void hpc_set_green_led_blink(struct slot *slot)
|
||||||
cmd_mask = cmd_mask | HP_INTR_ENABLE;
|
cmd_mask = cmd_mask | HP_INTR_ENABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
pcie_write_cmd(slot, slot_cmd, cmd_mask);
|
pcie_write_cmd(ctrl, slot_cmd, cmd_mask);
|
||||||
|
|
||||||
dbg("%s: SLOTCTRL %x write cmd %x\n",
|
dbg("%s: SLOTCTRL %x write cmd %x\n",
|
||||||
__func__, ctrl->cap_base + SLOTCTRL, slot_cmd);
|
__func__, ctrl->cap_base + SLOTCTRL, slot_cmd);
|
||||||
|
@ -620,7 +619,7 @@ static int hpc_power_on_slot(struct slot * slot)
|
||||||
HP_INTR_ENABLE;
|
HP_INTR_ENABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
retval = pcie_write_cmd(slot, slot_cmd, cmd_mask);
|
retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask);
|
||||||
|
|
||||||
if (retval) {
|
if (retval) {
|
||||||
err("%s: Write %x command failed!\n", __func__, slot_cmd);
|
err("%s: Write %x command failed!\n", __func__, slot_cmd);
|
||||||
|
@ -704,7 +703,7 @@ static int hpc_power_off_slot(struct slot * slot)
|
||||||
HP_INTR_ENABLE;
|
HP_INTR_ENABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
retval = pcie_write_cmd(slot, slot_cmd, cmd_mask);
|
retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask);
|
||||||
if (retval) {
|
if (retval) {
|
||||||
err("%s: Write command failed!\n", __func__);
|
err("%s: Write command failed!\n", __func__);
|
||||||
retval = -1;
|
retval = -1;
|
||||||
|
@ -1036,45 +1035,9 @@ int pciehp_acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev)
|
||||||
static int pcie_init_hardware_part1(struct controller *ctrl,
|
static int pcie_init_hardware_part1(struct controller *ctrl,
|
||||||
struct pcie_device *dev)
|
struct pcie_device *dev)
|
||||||
{
|
{
|
||||||
int rc;
|
|
||||||
u16 temp_word;
|
|
||||||
u32 slot_cap;
|
|
||||||
u16 slot_status;
|
|
||||||
|
|
||||||
rc = pciehp_readl(ctrl, SLOTCAP, &slot_cap);
|
|
||||||
if (rc) {
|
|
||||||
err("%s: Cannot read SLOTCAP register\n", __func__);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mask Hot-plug Interrupt Enable */
|
/* Mask Hot-plug Interrupt Enable */
|
||||||
rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word);
|
if (pcie_write_cmd(ctrl, 0, HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE)) {
|
||||||
if (rc) {
|
err("%s: Cannot mask hotplug interrupt enable\n", __func__);
|
||||||
err("%s: Cannot read SLOTCTRL register\n", __func__);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
dbg("%s: SLOTCTRL %x value read %x\n",
|
|
||||||
__func__, ctrl->cap_base + SLOTCTRL, temp_word);
|
|
||||||
temp_word = (temp_word & ~HP_INTR_ENABLE & ~CMD_CMPL_INTR_ENABLE) |
|
|
||||||
0x00;
|
|
||||||
|
|
||||||
rc = pciehp_writew(ctrl, SLOTCTRL, temp_word);
|
|
||||||
if (rc) {
|
|
||||||
err("%s: Cannot write to SLOTCTRL register\n", __func__);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status);
|
|
||||||
if (rc) {
|
|
||||||
err("%s: Cannot read SLOTSTATUS register\n", __func__);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
temp_word = 0x1F; /* Clear all events */
|
|
||||||
rc = pciehp_writew(ctrl, SLOTSTATUS, temp_word);
|
|
||||||
if (rc) {
|
|
||||||
err("%s: Cannot write to SLOTSTATUS register\n", __func__);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1082,84 +1045,47 @@ static int pcie_init_hardware_part1(struct controller *ctrl,
|
||||||
|
|
||||||
int pcie_init_hardware_part2(struct controller *ctrl, struct pcie_device *dev)
|
int pcie_init_hardware_part2(struct controller *ctrl, struct pcie_device *dev)
|
||||||
{
|
{
|
||||||
int rc;
|
u16 cmd, mask;
|
||||||
u16 temp_word;
|
|
||||||
u16 intr_enable = 0;
|
|
||||||
u32 slot_cap;
|
|
||||||
u16 slot_status;
|
|
||||||
|
|
||||||
rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word);
|
|
||||||
if (rc) {
|
|
||||||
err("%s: Cannot read SLOTCTRL register\n", __func__);
|
|
||||||
goto abort;
|
|
||||||
}
|
|
||||||
|
|
||||||
intr_enable = intr_enable | PRSN_DETECT_ENABLE;
|
|
||||||
|
|
||||||
rc = pciehp_readl(ctrl, SLOTCAP, &slot_cap);
|
|
||||||
if (rc) {
|
|
||||||
err("%s: Cannot read SLOTCAP register\n", __func__);
|
|
||||||
goto abort;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ATTN_BUTTN(slot_cap))
|
|
||||||
intr_enable = intr_enable | ATTN_BUTTN_ENABLE;
|
|
||||||
|
|
||||||
if (POWER_CTRL(slot_cap))
|
|
||||||
intr_enable = intr_enable | PWR_FAULT_DETECT_ENABLE;
|
|
||||||
|
|
||||||
if (MRL_SENS(slot_cap))
|
|
||||||
intr_enable = intr_enable | MRL_DETECT_ENABLE;
|
|
||||||
|
|
||||||
temp_word = (temp_word & ~intr_enable) | intr_enable;
|
|
||||||
|
|
||||||
if (pciehp_poll_mode) {
|
|
||||||
temp_word = (temp_word & ~HP_INTR_ENABLE) | 0x0;
|
|
||||||
} else {
|
|
||||||
temp_word = (temp_word & ~HP_INTR_ENABLE) | HP_INTR_ENABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Unmask Hot-plug Interrupt Enable for the interrupt
|
* We need to clear all events before enabling hotplug interrupt
|
||||||
* notification mechanism case.
|
* notification mechanism in order for hotplug controler to
|
||||||
|
* generate interrupts.
|
||||||
*/
|
*/
|
||||||
rc = pciehp_writew(ctrl, SLOTCTRL, temp_word);
|
if (pciehp_writew(ctrl, SLOTSTATUS, 0x1f)) {
|
||||||
if (rc) {
|
err("%s: Cannot write to SLOTSTATUS register\n", __FUNCTION__);
|
||||||
err("%s: Cannot write to SLOTCTRL register\n", __func__);
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = PRSN_DETECT_ENABLE;
|
||||||
|
if (ATTN_BUTTN(ctrl->ctrlcap))
|
||||||
|
cmd |= ATTN_BUTTN_ENABLE;
|
||||||
|
if (POWER_CTRL(ctrl->ctrlcap))
|
||||||
|
cmd |= PWR_FAULT_DETECT_ENABLE;
|
||||||
|
if (MRL_SENS(ctrl->ctrlcap))
|
||||||
|
cmd |= MRL_DETECT_ENABLE;
|
||||||
|
if (!pciehp_poll_mode)
|
||||||
|
cmd |= HP_INTR_ENABLE;
|
||||||
|
|
||||||
|
mask = PRSN_DETECT_ENABLE | ATTN_BUTTN_ENABLE |
|
||||||
|
PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE | HP_INTR_ENABLE;
|
||||||
|
|
||||||
|
if (pcie_write_cmd(ctrl, cmd, mask)) {
|
||||||
|
err("%s: Cannot enable software notification\n", __func__);
|
||||||
goto abort;
|
goto abort;
|
||||||
}
|
}
|
||||||
rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status);
|
|
||||||
if (rc) {
|
|
||||||
err("%s: Cannot read SLOTSTATUS register\n", __func__);
|
|
||||||
goto abort_disable_intr;
|
|
||||||
}
|
|
||||||
|
|
||||||
temp_word = 0x1F; /* Clear all events */
|
if (pciehp_force)
|
||||||
rc = pciehp_writew(ctrl, SLOTSTATUS, temp_word);
|
|
||||||
if (rc) {
|
|
||||||
err("%s: Cannot write to SLOTSTATUS register\n", __func__);
|
|
||||||
goto abort_disable_intr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pciehp_force) {
|
|
||||||
dbg("Bypassing BIOS check for pciehp use on %s\n",
|
dbg("Bypassing BIOS check for pciehp use on %s\n",
|
||||||
pci_name(ctrl->pci_dev));
|
pci_name(ctrl->pci_dev));
|
||||||
} else {
|
else if (pciehp_get_hp_hw_control_from_firmware(ctrl->pci_dev))
|
||||||
rc = pciehp_get_hp_hw_control_from_firmware(ctrl->pci_dev);
|
|
||||||
if (rc)
|
|
||||||
goto abort_disable_intr;
|
goto abort_disable_intr;
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* We end up here for the many possible ways to fail this API. */
|
/* We end up here for the many possible ways to fail this API. */
|
||||||
abort_disable_intr:
|
abort_disable_intr:
|
||||||
rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word);
|
if (pcie_write_cmd(ctrl, 0, HP_INTR_ENABLE))
|
||||||
if (!rc) {
|
|
||||||
temp_word &= ~(intr_enable | HP_INTR_ENABLE);
|
|
||||||
rc = pciehp_writew(ctrl, SLOTCTRL, temp_word);
|
|
||||||
}
|
|
||||||
if (rc)
|
|
||||||
err("%s : disabling interrupts failed\n", __func__);
|
err("%s : disabling interrupts failed\n", __func__);
|
||||||
abort:
|
abort:
|
||||||
return -1;
|
return -1;
|
||||||
|
|
Loading…
Reference in New Issue