diff options
author | Mark Lord <lkml@rtr.ca> | 2007-11-21 18:07:55 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-02-01 18:04:23 -0500 |
commit | ecdde93962eacd9c417977a4eabd318dbb612c11 (patch) | |
tree | 5637b661a33330cd7171f4965a7f430310dec48c /drivers/pci | |
parent | cd2fe83a81510acfd1ae29b8ffe04f7ef675c993 (diff) |
PCIe: fix double initialization bug
Earlier patches to split out the hardware init for PCIe hotplug resulted in
some one-time initializations being redone on every resume cycle. Eg.
irq/polling initialization.
This patch splits the hardware init into two parts, and separates the
one-time initializations from those so that they only ever get done once,
as intended.
Signed-off-by: Mark Lord <mlord@pobox.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/hotplug/pciehp.h | 2 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_core.c | 2 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_hpc.c | 119 |
3 files changed, 69 insertions, 54 deletions
diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index e4ad00a3448e..288fc4689103 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h | |||
@@ -163,7 +163,7 @@ extern void pciehp_queue_pushbutton_work(struct work_struct *work); | |||
163 | int pcie_init(struct controller *ctrl, struct pcie_device *dev); | 163 | int pcie_init(struct controller *ctrl, struct pcie_device *dev); |
164 | int pciehp_enable_slot(struct slot *p_slot); | 164 | int pciehp_enable_slot(struct slot *p_slot); |
165 | int pciehp_disable_slot(struct slot *p_slot); | 165 | int pciehp_disable_slot(struct slot *p_slot); |
166 | int pcie_init_hardware(struct controller *ctrl, struct pcie_device *dev); | 166 | int pcie_init_hardware_part2(struct controller *ctrl, struct pcie_device *dev); |
167 | 167 | ||
168 | static inline struct slot *pciehp_find_slot(struct controller *ctrl, u8 device) | 168 | static inline struct slot *pciehp_find_slot(struct controller *ctrl, u8 device) |
169 | { | 169 | { |
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index ae3fe318b8fd..310223d037aa 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c | |||
@@ -521,7 +521,7 @@ static int pciehp_resume (struct pcie_device *dev) | |||
521 | u8 status; | 521 | u8 status; |
522 | 522 | ||
523 | /* reinitialize the chipset's event detection logic */ | 523 | /* reinitialize the chipset's event detection logic */ |
524 | pcie_init_hardware(ctrl, dev); | 524 | pcie_init_hardware_part2(ctrl, dev); |
525 | 525 | ||
526 | t_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset); | 526 | t_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset); |
527 | 527 | ||
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 8b11d80bf651..8c2d6c9b92c5 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c | |||
@@ -1067,28 +1067,25 @@ int pciehp_acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev) | |||
1067 | } | 1067 | } |
1068 | #endif | 1068 | #endif |
1069 | 1069 | ||
1070 | int pcie_init_hardware(struct controller *ctrl, struct pcie_device *dev) | 1070 | static int pcie_init_hardware_part1(struct controller *ctrl, |
1071 | struct pcie_device *dev) | ||
1071 | { | 1072 | { |
1072 | int rc; | 1073 | int rc; |
1073 | u16 temp_word; | 1074 | u16 temp_word; |
1074 | u16 intr_enable = 0; | ||
1075 | u32 slot_cap; | 1075 | u32 slot_cap; |
1076 | u16 slot_status; | 1076 | u16 slot_status; |
1077 | struct pci_dev *pdev; | ||
1078 | |||
1079 | pdev = dev->port; | ||
1080 | 1077 | ||
1081 | rc = pciehp_readl(ctrl, SLOTCAP, &slot_cap); | 1078 | rc = pciehp_readl(ctrl, SLOTCAP, &slot_cap); |
1082 | if (rc) { | 1079 | if (rc) { |
1083 | err("%s: Cannot read SLOTCAP register\n", __FUNCTION__); | 1080 | err("%s: Cannot read SLOTCAP register\n", __FUNCTION__); |
1084 | goto abort_free_ctlr; | 1081 | return -1; |
1085 | } | 1082 | } |
1086 | 1083 | ||
1087 | /* Mask Hot-plug Interrupt Enable */ | 1084 | /* Mask Hot-plug Interrupt Enable */ |
1088 | rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word); | 1085 | rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word); |
1089 | if (rc) { | 1086 | if (rc) { |
1090 | err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__); | 1087 | err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__); |
1091 | goto abort_free_ctlr; | 1088 | return -1; |
1092 | } | 1089 | } |
1093 | 1090 | ||
1094 | dbg("%s: SLOTCTRL %x value read %x\n", | 1091 | dbg("%s: SLOTCTRL %x value read %x\n", |
@@ -1099,62 +1096,46 @@ int pcie_init_hardware(struct controller *ctrl, struct pcie_device *dev) | |||
1099 | rc = pciehp_writew(ctrl, SLOTCTRL, temp_word); | 1096 | rc = pciehp_writew(ctrl, SLOTCTRL, temp_word); |
1100 | if (rc) { | 1097 | if (rc) { |
1101 | err("%s: Cannot write to SLOTCTRL register\n", __FUNCTION__); | 1098 | err("%s: Cannot write to SLOTCTRL register\n", __FUNCTION__); |
1102 | goto abort_free_ctlr; | 1099 | return -1; |
1103 | } | 1100 | } |
1104 | 1101 | ||
1105 | rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); | 1102 | rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); |
1106 | if (rc) { | 1103 | if (rc) { |
1107 | err("%s: Cannot read SLOTSTATUS register\n", __FUNCTION__); | 1104 | err("%s: Cannot read SLOTSTATUS register\n", __FUNCTION__); |
1108 | goto abort_free_ctlr; | 1105 | return -1; |
1109 | } | 1106 | } |
1110 | 1107 | ||
1111 | temp_word = 0x1F; /* Clear all events */ | 1108 | temp_word = 0x1F; /* Clear all events */ |
1112 | rc = pciehp_writew(ctrl, SLOTSTATUS, temp_word); | 1109 | rc = pciehp_writew(ctrl, SLOTSTATUS, temp_word); |
1113 | if (rc) { | 1110 | if (rc) { |
1114 | err("%s: Cannot write to SLOTSTATUS register\n", __FUNCTION__); | 1111 | err("%s: Cannot write to SLOTSTATUS register\n", __FUNCTION__); |
1115 | goto abort_free_ctlr; | 1112 | return -1; |
1116 | } | ||
1117 | |||
1118 | if (pciehp_poll_mode) { | ||
1119 | /* Install interrupt polling timer. Start with 10 sec delay */ | ||
1120 | init_timer(&ctrl->poll_timer); | ||
1121 | start_int_poll_timer(ctrl, 10); | ||
1122 | } else { | ||
1123 | /* Installs the interrupt handler */ | ||
1124 | rc = request_irq(ctrl->pci_dev->irq, pcie_isr, IRQF_SHARED, | ||
1125 | MY_NAME, (void *)ctrl); | ||
1126 | dbg("%s: request_irq %d for hpc%d (returns %d)\n", | ||
1127 | __FUNCTION__, ctrl->pci_dev->irq, | ||
1128 | atomic_read(&pciehp_num_controllers), rc); | ||
1129 | if (rc) { | ||
1130 | err("Can't get irq %d for the hotplug controller\n", | ||
1131 | ctrl->pci_dev->irq); | ||
1132 | goto abort_free_ctlr; | ||
1133 | } | ||
1134 | } | 1113 | } |
1135 | dbg("pciehp ctrl b:d:f:irq=0x%x:%x:%x:%x\n", pdev->bus->number, | 1114 | return 0; |
1136 | PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), dev->irq); | 1115 | } |
1137 | 1116 | ||
1138 | /* | 1117 | int pcie_init_hardware_part2(struct controller *ctrl, struct pcie_device *dev) |
1139 | * If this is the first controller to be initialized, | 1118 | { |
1140 | * initialize the pciehp work queue | 1119 | int rc; |
1141 | */ | 1120 | u16 temp_word; |
1142 | if (atomic_add_return(1, &pciehp_num_controllers) == 1) { | 1121 | u16 intr_enable = 0; |
1143 | pciehp_wq = create_singlethread_workqueue("pciehpd"); | 1122 | u32 slot_cap; |
1144 | if (!pciehp_wq) { | 1123 | u16 slot_status; |
1145 | rc = -ENOMEM; | ||
1146 | goto abort_free_irq; | ||
1147 | } | ||
1148 | } | ||
1149 | 1124 | ||
1150 | rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word); | 1125 | rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word); |
1151 | if (rc) { | 1126 | if (rc) { |
1152 | err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__); | 1127 | err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__); |
1153 | goto abort_free_irq; | 1128 | goto abort; |
1154 | } | 1129 | } |
1155 | 1130 | ||
1156 | intr_enable = intr_enable | PRSN_DETECT_ENABLE; | 1131 | intr_enable = intr_enable | PRSN_DETECT_ENABLE; |
1157 | 1132 | ||
1133 | rc = pciehp_readl(ctrl, SLOTCAP, &slot_cap); | ||
1134 | if (rc) { | ||
1135 | err("%s: Cannot read SLOTCAP register\n", __FUNCTION__); | ||
1136 | goto abort; | ||
1137 | } | ||
1138 | |||
1158 | if (ATTN_BUTTN(slot_cap)) | 1139 | if (ATTN_BUTTN(slot_cap)) |
1159 | intr_enable = intr_enable | ATTN_BUTTN_ENABLE; | 1140 | intr_enable = intr_enable | ATTN_BUTTN_ENABLE; |
1160 | 1141 | ||
@@ -1179,7 +1160,7 @@ int pcie_init_hardware(struct controller *ctrl, struct pcie_device *dev) | |||
1179 | rc = pciehp_writew(ctrl, SLOTCTRL, temp_word); | 1160 | rc = pciehp_writew(ctrl, SLOTCTRL, temp_word); |
1180 | if (rc) { | 1161 | if (rc) { |
1181 | err("%s: Cannot write to SLOTCTRL register\n", __FUNCTION__); | 1162 | err("%s: Cannot write to SLOTCTRL register\n", __FUNCTION__); |
1182 | goto abort_free_irq; | 1163 | goto abort; |
1183 | } | 1164 | } |
1184 | rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); | 1165 | rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); |
1185 | if (rc) { | 1166 | if (rc) { |
@@ -1214,14 +1195,7 @@ abort_disable_intr: | |||
1214 | } | 1195 | } |
1215 | if (rc) | 1196 | if (rc) |
1216 | err("%s : disabling interrupts failed\n", __FUNCTION__); | 1197 | err("%s : disabling interrupts failed\n", __FUNCTION__); |
1217 | 1198 | abort: | |
1218 | abort_free_irq: | ||
1219 | if (pciehp_poll_mode) | ||
1220 | del_timer_sync(&ctrl->poll_timer); | ||
1221 | else | ||
1222 | free_irq(ctrl->pci_dev->irq, ctrl); | ||
1223 | |||
1224 | abort_free_ctlr: | ||
1225 | return -1; | 1199 | return -1; |
1226 | } | 1200 | } |
1227 | 1201 | ||
@@ -1318,11 +1292,52 @@ int pcie_init(struct controller *ctrl, struct pcie_device *dev) | |||
1318 | ctrl->first_slot = slot_cap >> 19; | 1292 | ctrl->first_slot = slot_cap >> 19; |
1319 | ctrl->ctrlcap = slot_cap & 0x0000007f; | 1293 | ctrl->ctrlcap = slot_cap & 0x0000007f; |
1320 | 1294 | ||
1321 | rc = pcie_init_hardware(ctrl, dev); | 1295 | rc = pcie_init_hardware_part1(ctrl, dev); |
1296 | if (rc) | ||
1297 | goto abort; | ||
1298 | |||
1299 | if (pciehp_poll_mode) { | ||
1300 | /* Install interrupt polling timer. Start with 10 sec delay */ | ||
1301 | init_timer(&ctrl->poll_timer); | ||
1302 | start_int_poll_timer(ctrl, 10); | ||
1303 | } else { | ||
1304 | /* Installs the interrupt handler */ | ||
1305 | rc = request_irq(ctrl->pci_dev->irq, pcie_isr, IRQF_SHARED, | ||
1306 | MY_NAME, (void *)ctrl); | ||
1307 | dbg("%s: request_irq %d for hpc%d (returns %d)\n", | ||
1308 | __FUNCTION__, ctrl->pci_dev->irq, | ||
1309 | atomic_read(&pciehp_num_controllers), rc); | ||
1310 | if (rc) { | ||
1311 | err("Can't get irq %d for the hotplug controller\n", | ||
1312 | ctrl->pci_dev->irq); | ||
1313 | goto abort; | ||
1314 | } | ||
1315 | } | ||
1316 | dbg("pciehp ctrl b:d:f:irq=0x%x:%x:%x:%x\n", pdev->bus->number, | ||
1317 | PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), dev->irq); | ||
1318 | |||
1319 | /* | ||
1320 | * If this is the first controller to be initialized, | ||
1321 | * initialize the pciehp work queue | ||
1322 | */ | ||
1323 | if (atomic_add_return(1, &pciehp_num_controllers) == 1) { | ||
1324 | pciehp_wq = create_singlethread_workqueue("pciehpd"); | ||
1325 | if (!pciehp_wq) { | ||
1326 | rc = -ENOMEM; | ||
1327 | goto abort_free_irq; | ||
1328 | } | ||
1329 | } | ||
1330 | |||
1331 | rc = pcie_init_hardware_part2(ctrl, dev); | ||
1322 | if (rc == 0) { | 1332 | if (rc == 0) { |
1323 | ctrl->hpc_ops = &pciehp_hpc_ops; | 1333 | ctrl->hpc_ops = &pciehp_hpc_ops; |
1324 | return 0; | 1334 | return 0; |
1325 | } | 1335 | } |
1336 | abort_free_irq: | ||
1337 | if (pciehp_poll_mode) | ||
1338 | del_timer_sync(&ctrl->poll_timer); | ||
1339 | else | ||
1340 | free_irq(ctrl->pci_dev->irq, ctrl); | ||
1326 | abort: | 1341 | abort: |
1327 | return -1; | 1342 | return -1; |
1328 | } | 1343 | } |