Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Kenji Kaneshige and committed by
Jesse Barnes
c27fb883 2d32a9ae

+39 -113
+39 -113
drivers/pci/hotplug/pciehp_hpc.c
··· 242 242 243 243 /** 244 244 * pcie_write_cmd - Issue controller command 245 - * @slot: slot to which the command is issued 245 + * @ctrl: controller to which the command is issued 246 246 * @cmd: command value written to slot control register 247 247 * @mask: bitmask of slot control register to be modified 248 248 */ 249 - static int pcie_write_cmd(struct slot *slot, u16 cmd, u16 mask) 249 + static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) 250 250 { 251 - struct controller *ctrl = slot->ctrl; 252 251 int retval = 0; 253 252 u16 slot_status; 254 253 u16 slot_ctrl; ··· 467 468 cmd_mask = cmd_mask | HP_INTR_ENABLE; 468 469 } 469 470 470 - rc = pcie_write_cmd(slot, slot_cmd, cmd_mask); 471 + rc = pcie_write_cmd(slot->ctrl, slot_cmd, cmd_mask); 471 472 slot->last_emi_toggle = get_seconds(); 472 473 473 474 return rc; ··· 499 500 cmd_mask = cmd_mask | HP_INTR_ENABLE; 500 501 } 501 502 502 - rc = pcie_write_cmd(slot, slot_cmd, cmd_mask); 503 + rc = pcie_write_cmd(ctrl, slot_cmd, cmd_mask); 503 504 dbg("%s: SLOTCTRL %x write cmd %x\n", 504 505 __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); 505 506 ··· 519 520 cmd_mask = cmd_mask | HP_INTR_ENABLE; 520 521 } 521 522 522 - pcie_write_cmd(slot, slot_cmd, cmd_mask); 523 + pcie_write_cmd(ctrl, slot_cmd, cmd_mask); 523 524 524 525 dbg("%s: SLOTCTRL %x write cmd %x\n", 525 526 __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); ··· 538 539 cmd_mask = cmd_mask | HP_INTR_ENABLE; 539 540 } 540 541 541 - pcie_write_cmd(slot, slot_cmd, cmd_mask); 542 + pcie_write_cmd(ctrl, slot_cmd, cmd_mask); 542 543 dbg("%s: SLOTCTRL %x write cmd %x\n", 543 544 __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); 544 545 } ··· 556 557 cmd_mask = cmd_mask | HP_INTR_ENABLE; 557 558 } 558 559 559 - pcie_write_cmd(slot, slot_cmd, cmd_mask); 560 + pcie_write_cmd(ctrl, slot_cmd, cmd_mask); 560 561 561 562 dbg("%s: SLOTCTRL %x write cmd %x\n", 562 563 __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); ··· 619 620 HP_INTR_ENABLE; 620 621 } 621 622 622 - retval = pcie_write_cmd(slot, slot_cmd, cmd_mask); 623 + retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask); 623 624 624 625 if (retval) { 625 626 err("%s: Write %x command failed!\n", __func__, slot_cmd); ··· 703 704 HP_INTR_ENABLE; 704 705 } 705 706 706 - retval = pcie_write_cmd(slot, slot_cmd, cmd_mask); 707 + retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask); 707 708 if (retval) { 708 709 err("%s: Write command failed!\n", __func__); 709 710 retval = -1; ··· 1035 1036 static int pcie_init_hardware_part1(struct controller *ctrl, 1036 1037 struct pcie_device *dev) 1037 1038 { 1038 - int rc; 1039 - u16 temp_word; 1040 - u32 slot_cap; 1041 - u16 slot_status; 1042 - 1043 - rc = pciehp_readl(ctrl, SLOTCAP, &slot_cap); 1044 - if (rc) { 1045 - err("%s: Cannot read SLOTCAP register\n", __func__); 1046 - return -1; 1047 - } 1048 - 1049 1039 /* Mask Hot-plug Interrupt Enable */ 1050 - rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word); 1051 - if (rc) { 1052 - err("%s: Cannot read SLOTCTRL register\n", __func__); 1053 - return -1; 1054 - } 1055 - 1056 - dbg("%s: SLOTCTRL %x value read %x\n", 1057 - __func__, ctrl->cap_base + SLOTCTRL, temp_word); 1058 - temp_word = (temp_word & ~HP_INTR_ENABLE & ~CMD_CMPL_INTR_ENABLE) | 1059 - 0x00; 1060 - 1061 - rc = pciehp_writew(ctrl, SLOTCTRL, temp_word); 1062 - if (rc) { 1063 - err("%s: Cannot write to SLOTCTRL register\n", __func__); 1064 - return -1; 1065 - } 1066 - 1067 - rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); 1068 - if (rc) { 1069 - err("%s: Cannot read SLOTSTATUS register\n", __func__); 1070 - return -1; 1071 - } 1072 - 1073 - temp_word = 0x1F; /* Clear all events */ 1074 - rc = pciehp_writew(ctrl, SLOTSTATUS, temp_word); 1075 - if (rc) { 1076 - err("%s: Cannot write to SLOTSTATUS register\n", __func__); 1040 + if (pcie_write_cmd(ctrl, 0, HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE)) { 1041 + err("%s: Cannot mask hotplug interrupt enable\n", __func__); 1077 1042 return -1; 1078 1043 } 1079 1044 return 0; ··· 1045 1082 1046 1083 int pcie_init_hardware_part2(struct controller *ctrl, struct pcie_device *dev) 1047 1084 { 1048 - int rc; 1049 - u16 temp_word; 1050 - u16 intr_enable = 0; 1051 - u32 slot_cap; 1052 - u16 slot_status; 1053 - 1054 - rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word); 1055 - if (rc) { 1056 - err("%s: Cannot read SLOTCTRL register\n", __func__); 1057 - goto abort; 1058 - } 1059 - 1060 - intr_enable = intr_enable | PRSN_DETECT_ENABLE; 1061 - 1062 - rc = pciehp_readl(ctrl, SLOTCAP, &slot_cap); 1063 - if (rc) { 1064 - err("%s: Cannot read SLOTCAP register\n", __func__); 1065 - goto abort; 1066 - } 1067 - 1068 - if (ATTN_BUTTN(slot_cap)) 1069 - intr_enable = intr_enable | ATTN_BUTTN_ENABLE; 1070 - 1071 - if (POWER_CTRL(slot_cap)) 1072 - intr_enable = intr_enable | PWR_FAULT_DETECT_ENABLE; 1073 - 1074 - if (MRL_SENS(slot_cap)) 1075 - intr_enable = intr_enable | MRL_DETECT_ENABLE; 1076 - 1077 - temp_word = (temp_word & ~intr_enable) | intr_enable; 1078 - 1079 - if (pciehp_poll_mode) { 1080 - temp_word = (temp_word & ~HP_INTR_ENABLE) | 0x0; 1081 - } else { 1082 - temp_word = (temp_word & ~HP_INTR_ENABLE) | HP_INTR_ENABLE; 1083 - } 1085 + u16 cmd, mask; 1084 1086 1085 1087 /* 1086 - * Unmask Hot-plug Interrupt Enable for the interrupt 1087 - * notification mechanism case. 1088 + * We need to clear all events before enabling hotplug interrupt 1089 + * notification mechanism in order for hotplug controler to 1090 + * generate interrupts. 1088 1091 */ 1089 - rc = pciehp_writew(ctrl, SLOTCTRL, temp_word); 1090 - if (rc) { 1091 - err("%s: Cannot write to SLOTCTRL register\n", __func__); 1092 + if (pciehp_writew(ctrl, SLOTSTATUS, 0x1f)) { 1093 + err("%s: Cannot write to SLOTSTATUS register\n", __FUNCTION__); 1094 + return -1; 1095 + } 1096 + 1097 + cmd = PRSN_DETECT_ENABLE; 1098 + if (ATTN_BUTTN(ctrl->ctrlcap)) 1099 + cmd |= ATTN_BUTTN_ENABLE; 1100 + if (POWER_CTRL(ctrl->ctrlcap)) 1101 + cmd |= PWR_FAULT_DETECT_ENABLE; 1102 + if (MRL_SENS(ctrl->ctrlcap)) 1103 + cmd |= MRL_DETECT_ENABLE; 1104 + if (!pciehp_poll_mode) 1105 + cmd |= HP_INTR_ENABLE; 1106 + 1107 + mask = PRSN_DETECT_ENABLE | ATTN_BUTTN_ENABLE | 1108 + PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE | HP_INTR_ENABLE; 1109 + 1110 + if (pcie_write_cmd(ctrl, cmd, mask)) { 1111 + err("%s: Cannot enable software notification\n", __func__); 1092 1112 goto abort; 1093 1113 } 1094 - rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); 1095 - if (rc) { 1096 - err("%s: Cannot read SLOTSTATUS register\n", __func__); 1097 - goto abort_disable_intr; 1098 - } 1099 1114 1100 - temp_word = 0x1F; /* Clear all events */ 1101 - rc = pciehp_writew(ctrl, SLOTSTATUS, temp_word); 1102 - if (rc) { 1103 - err("%s: Cannot write to SLOTSTATUS register\n", __func__); 1104 - goto abort_disable_intr; 1105 - } 1106 - 1107 - if (pciehp_force) { 1115 + if (pciehp_force) 1108 1116 dbg("Bypassing BIOS check for pciehp use on %s\n", 1109 1117 pci_name(ctrl->pci_dev)); 1110 - } else { 1111 - rc = pciehp_get_hp_hw_control_from_firmware(ctrl->pci_dev); 1112 - if (rc) 1113 - goto abort_disable_intr; 1114 - } 1118 + else if (pciehp_get_hp_hw_control_from_firmware(ctrl->pci_dev)) 1119 + goto abort_disable_intr; 1115 1120 1116 1121 return 0; 1117 1122 1118 1123 /* We end up here for the many possible ways to fail this API. */ 1119 1124 abort_disable_intr: 1120 - rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word); 1121 - if (!rc) { 1122 - temp_word &= ~(intr_enable | HP_INTR_ENABLE); 1123 - rc = pciehp_writew(ctrl, SLOTCTRL, temp_word); 1124 - } 1125 - if (rc) 1125 + if (pcie_write_cmd(ctrl, 0, HP_INTR_ENABLE)) 1126 1126 err("%s : disabling interrupts failed\n", __func__); 1127 1127 abort: 1128 1128 return -1;