diff options
Diffstat (limited to 'drivers/pci/hotplug/pciehp_hpc.c')
-rw-r--r-- | drivers/pci/hotplug/pciehp_hpc.c | 133 |
1 files changed, 109 insertions, 24 deletions
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index bcdbb164362..a960faec102 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c | |||
@@ -241,34 +241,79 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) | |||
241 | return retval; | 241 | return retval; |
242 | } | 242 | } |
243 | 243 | ||
244 | static inline int check_link_active(struct controller *ctrl) | 244 | static bool check_link_active(struct controller *ctrl) |
245 | { | 245 | { |
246 | u16 link_status; | 246 | bool ret = false; |
247 | u16 lnk_status; | ||
247 | 248 | ||
248 | if (pciehp_readw(ctrl, PCI_EXP_LNKSTA, &link_status)) | 249 | if (pciehp_readw(ctrl, PCI_EXP_LNKSTA, &lnk_status)) |
249 | return 0; | 250 | return ret; |
250 | return !!(link_status & PCI_EXP_LNKSTA_DLLLA); | 251 | |
252 | ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA); | ||
253 | |||
254 | if (ret) | ||
255 | ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status); | ||
256 | |||
257 | return ret; | ||
251 | } | 258 | } |
252 | 259 | ||
253 | static void pcie_wait_link_active(struct controller *ctrl) | 260 | static void __pcie_wait_link_active(struct controller *ctrl, bool active) |
254 | { | 261 | { |
255 | int timeout = 1000; | 262 | int timeout = 1000; |
256 | 263 | ||
257 | if (check_link_active(ctrl)) | 264 | if (check_link_active(ctrl) == active) |
258 | return; | 265 | return; |
259 | while (timeout > 0) { | 266 | while (timeout > 0) { |
260 | msleep(10); | 267 | msleep(10); |
261 | timeout -= 10; | 268 | timeout -= 10; |
262 | if (check_link_active(ctrl)) | 269 | if (check_link_active(ctrl) == active) |
263 | return; | 270 | return; |
264 | } | 271 | } |
265 | ctrl_dbg(ctrl, "Data Link Layer Link Active not set in 1000 msec\n"); | 272 | ctrl_dbg(ctrl, "Data Link Layer Link Active not %s in 1000 msec\n", |
273 | active ? "set" : "cleared"); | ||
274 | } | ||
275 | |||
276 | static void pcie_wait_link_active(struct controller *ctrl) | ||
277 | { | ||
278 | __pcie_wait_link_active(ctrl, true); | ||
279 | } | ||
280 | |||
281 | static void pcie_wait_link_not_active(struct controller *ctrl) | ||
282 | { | ||
283 | __pcie_wait_link_active(ctrl, false); | ||
284 | } | ||
285 | |||
286 | static bool pci_bus_check_dev(struct pci_bus *bus, int devfn) | ||
287 | { | ||
288 | u32 l; | ||
289 | int count = 0; | ||
290 | int delay = 1000, step = 20; | ||
291 | bool found = false; | ||
292 | |||
293 | do { | ||
294 | found = pci_bus_read_dev_vendor_id(bus, devfn, &l, 0); | ||
295 | count++; | ||
296 | |||
297 | if (found) | ||
298 | break; | ||
299 | |||
300 | msleep(step); | ||
301 | delay -= step; | ||
302 | } while (delay > 0); | ||
303 | |||
304 | if (count > 1 && pciehp_debug) | ||
305 | printk(KERN_DEBUG "pci %04x:%02x:%02x.%d id reading try %d times with interval %d ms to get %08x\n", | ||
306 | pci_domain_nr(bus), bus->number, PCI_SLOT(devfn), | ||
307 | PCI_FUNC(devfn), count, step, l); | ||
308 | |||
309 | return found; | ||
266 | } | 310 | } |
267 | 311 | ||
268 | int pciehp_check_link_status(struct controller *ctrl) | 312 | int pciehp_check_link_status(struct controller *ctrl) |
269 | { | 313 | { |
270 | u16 lnk_status; | 314 | u16 lnk_status; |
271 | int retval = 0; | 315 | int retval = 0; |
316 | bool found = false; | ||
272 | 317 | ||
273 | /* | 318 | /* |
274 | * Data Link Layer Link Active Reporting must be capable for | 319 | * Data Link Layer Link Active Reporting must be capable for |
@@ -280,13 +325,10 @@ int pciehp_check_link_status(struct controller *ctrl) | |||
280 | else | 325 | else |
281 | msleep(1000); | 326 | msleep(1000); |
282 | 327 | ||
283 | /* | 328 | /* wait 100ms before read pci conf, and try in 1s */ |
284 | * Need to wait for 1000 ms after Data Link Layer Link Active | 329 | msleep(100); |
285 | * (DLLLA) bit reads 1b before sending configuration request. | 330 | found = pci_bus_check_dev(ctrl->pcie->port->subordinate, |
286 | * We need it before checking Link Training (LT) bit becuase | 331 | PCI_DEVFN(0, 0)); |
287 | * LT is still set even after DLLLA bit is set on some platform. | ||
288 | */ | ||
289 | msleep(1000); | ||
290 | 332 | ||
291 | retval = pciehp_readw(ctrl, PCI_EXP_LNKSTA, &lnk_status); | 333 | retval = pciehp_readw(ctrl, PCI_EXP_LNKSTA, &lnk_status); |
292 | if (retval) { | 334 | if (retval) { |
@@ -302,19 +344,50 @@ int pciehp_check_link_status(struct controller *ctrl) | |||
302 | return retval; | 344 | return retval; |
303 | } | 345 | } |
304 | 346 | ||
305 | /* | ||
306 | * If the port supports Link speeds greater than 5.0 GT/s, we | ||
307 | * must wait for 100 ms after Link training completes before | ||
308 | * sending configuration request. | ||
309 | */ | ||
310 | if (ctrl->pcie->port->subordinate->max_bus_speed > PCIE_SPEED_5_0GT) | ||
311 | msleep(100); | ||
312 | |||
313 | pcie_update_link_speed(ctrl->pcie->port->subordinate, lnk_status); | 347 | pcie_update_link_speed(ctrl->pcie->port->subordinate, lnk_status); |
314 | 348 | ||
349 | if (!found && !retval) | ||
350 | retval = -1; | ||
351 | |||
315 | return retval; | 352 | return retval; |
316 | } | 353 | } |
317 | 354 | ||
355 | static int __pciehp_link_set(struct controller *ctrl, bool enable) | ||
356 | { | ||
357 | u16 lnk_ctrl; | ||
358 | int retval = 0; | ||
359 | |||
360 | retval = pciehp_readw(ctrl, PCI_EXP_LNKCTL, &lnk_ctrl); | ||
361 | if (retval) { | ||
362 | ctrl_err(ctrl, "Cannot read LNKCTRL register\n"); | ||
363 | return retval; | ||
364 | } | ||
365 | |||
366 | if (enable) | ||
367 | lnk_ctrl &= ~PCI_EXP_LNKCTL_LD; | ||
368 | else | ||
369 | lnk_ctrl |= PCI_EXP_LNKCTL_LD; | ||
370 | |||
371 | retval = pciehp_writew(ctrl, PCI_EXP_LNKCTL, lnk_ctrl); | ||
372 | if (retval) { | ||
373 | ctrl_err(ctrl, "Cannot write LNKCTRL register\n"); | ||
374 | return retval; | ||
375 | } | ||
376 | ctrl_dbg(ctrl, "%s: lnk_ctrl = %x\n", __func__, lnk_ctrl); | ||
377 | |||
378 | return retval; | ||
379 | } | ||
380 | |||
381 | static int pciehp_link_enable(struct controller *ctrl) | ||
382 | { | ||
383 | return __pciehp_link_set(ctrl, true); | ||
384 | } | ||
385 | |||
386 | static int pciehp_link_disable(struct controller *ctrl) | ||
387 | { | ||
388 | return __pciehp_link_set(ctrl, false); | ||
389 | } | ||
390 | |||
318 | int pciehp_get_attention_status(struct slot *slot, u8 *status) | 391 | int pciehp_get_attention_status(struct slot *slot, u8 *status) |
319 | { | 392 | { |
320 | struct controller *ctrl = slot->ctrl; | 393 | struct controller *ctrl = slot->ctrl; |
@@ -533,6 +606,10 @@ int pciehp_power_on_slot(struct slot * slot) | |||
533 | ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, | 606 | ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, |
534 | pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); | 607 | pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); |
535 | 608 | ||
609 | retval = pciehp_link_enable(ctrl); | ||
610 | if (retval) | ||
611 | ctrl_err(ctrl, "%s: Can not enable the link!\n", __func__); | ||
612 | |||
536 | return retval; | 613 | return retval; |
537 | } | 614 | } |
538 | 615 | ||
@@ -543,6 +620,14 @@ int pciehp_power_off_slot(struct slot * slot) | |||
543 | u16 cmd_mask; | 620 | u16 cmd_mask; |
544 | int retval; | 621 | int retval; |
545 | 622 | ||
623 | /* Disable the link at first */ | ||
624 | pciehp_link_disable(ctrl); | ||
625 | /* wait the link is down */ | ||
626 | if (ctrl->link_active_reporting) | ||
627 | pcie_wait_link_not_active(ctrl); | ||
628 | else | ||
629 | msleep(1000); | ||
630 | |||
546 | slot_cmd = POWER_OFF; | 631 | slot_cmd = POWER_OFF; |
547 | cmd_mask = PCI_EXP_SLTCTL_PCC; | 632 | cmd_mask = PCI_EXP_SLTCTL_PCC; |
548 | retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask); | 633 | retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask); |