.. | .. |
---|
18 | 18 | #define DRIVER_AUTHOR "Gavin Shan, IBM Corporation" |
---|
19 | 19 | #define DRIVER_DESC "PowerPC PowerNV PCI Hotplug Driver" |
---|
20 | 20 | |
---|
| 21 | +#define SLOT_WARN(sl, x...) \ |
---|
| 22 | + ((sl)->pdev ? pci_warn((sl)->pdev, x) : dev_warn(&(sl)->bus->dev, x)) |
---|
| 23 | + |
---|
21 | 24 | struct pnv_php_event { |
---|
22 | 25 | bool added; |
---|
23 | 26 | struct pnv_php_slot *php_slot; |
---|
.. | .. |
---|
151 | 154 | static void pnv_php_detach_device_nodes(struct device_node *parent) |
---|
152 | 155 | { |
---|
153 | 156 | struct device_node *dn; |
---|
154 | | - int refcount; |
---|
155 | 157 | |
---|
156 | 158 | for_each_child_of_node(parent, dn) { |
---|
157 | 159 | pnv_php_detach_device_nodes(dn); |
---|
158 | 160 | |
---|
159 | 161 | of_node_put(dn); |
---|
160 | | - refcount = kref_read(&dn->kobj.kref); |
---|
161 | | - if (refcount != 1) |
---|
162 | | - pr_warn("Invalid refcount %d on <%pOF>\n", |
---|
163 | | - refcount, dn); |
---|
164 | | - |
---|
165 | 162 | of_detach_node(dn); |
---|
166 | 163 | } |
---|
167 | 164 | } |
---|
.. | .. |
---|
271 | 268 | |
---|
272 | 269 | ret = pnv_pci_get_device_tree(php_slot->dn->phandle, fdt1, 0x10000); |
---|
273 | 270 | if (ret) { |
---|
274 | | - pci_warn(php_slot->pdev, "Error %d getting FDT blob\n", ret); |
---|
| 271 | + SLOT_WARN(php_slot, "Error %d getting FDT blob\n", ret); |
---|
275 | 272 | goto free_fdt1; |
---|
276 | 273 | } |
---|
277 | 274 | |
---|
278 | | - fdt = kzalloc(fdt_totalsize(fdt1), GFP_KERNEL); |
---|
| 275 | + fdt = kmemdup(fdt1, fdt_totalsize(fdt1), GFP_KERNEL); |
---|
279 | 276 | if (!fdt) { |
---|
280 | 277 | ret = -ENOMEM; |
---|
281 | 278 | goto free_fdt1; |
---|
282 | 279 | } |
---|
283 | 280 | |
---|
284 | 281 | /* Unflatten device tree blob */ |
---|
285 | | - memcpy(fdt, fdt1, fdt_totalsize(fdt1)); |
---|
286 | 282 | dt = of_fdt_unflatten_tree(fdt, php_slot->dn, NULL); |
---|
287 | 283 | if (!dt) { |
---|
288 | 284 | ret = -EINVAL; |
---|
289 | | - pci_warn(php_slot->pdev, "Cannot unflatten FDT\n"); |
---|
| 285 | + SLOT_WARN(php_slot, "Cannot unflatten FDT\n"); |
---|
290 | 286 | goto free_fdt; |
---|
291 | 287 | } |
---|
292 | 288 | |
---|
.. | .. |
---|
296 | 292 | ret = pnv_php_populate_changeset(&php_slot->ocs, php_slot->dn); |
---|
297 | 293 | if (ret) { |
---|
298 | 294 | pnv_php_reverse_nodes(php_slot->dn); |
---|
299 | | - pci_warn(php_slot->pdev, "Error %d populating changeset\n", |
---|
300 | | - ret); |
---|
| 295 | + SLOT_WARN(php_slot, "Error %d populating changeset\n", |
---|
| 296 | + ret); |
---|
301 | 297 | goto free_dt; |
---|
302 | 298 | } |
---|
303 | 299 | |
---|
304 | 300 | php_slot->dn->child = NULL; |
---|
305 | 301 | ret = of_changeset_apply(&php_slot->ocs); |
---|
306 | 302 | if (ret) { |
---|
307 | | - pci_warn(php_slot->pdev, "Error %d applying changeset\n", ret); |
---|
| 303 | + SLOT_WARN(php_slot, "Error %d applying changeset\n", ret); |
---|
308 | 304 | goto destroy_changeset; |
---|
309 | 305 | } |
---|
310 | 306 | |
---|
.. | .. |
---|
328 | 324 | return ret; |
---|
329 | 325 | } |
---|
330 | 326 | |
---|
| 327 | +static inline struct pnv_php_slot *to_pnv_php_slot(struct hotplug_slot *slot) |
---|
| 328 | +{ |
---|
| 329 | + return container_of(slot, struct pnv_php_slot, slot); |
---|
| 330 | +} |
---|
| 331 | + |
---|
331 | 332 | int pnv_php_set_slot_power_state(struct hotplug_slot *slot, |
---|
332 | 333 | uint8_t state) |
---|
333 | 334 | { |
---|
334 | | - struct pnv_php_slot *php_slot = slot->private; |
---|
| 335 | + struct pnv_php_slot *php_slot = to_pnv_php_slot(slot); |
---|
335 | 336 | struct opal_msg msg; |
---|
336 | 337 | int ret; |
---|
337 | 338 | |
---|
338 | 339 | ret = pnv_pci_set_power_state(php_slot->id, state, &msg); |
---|
339 | 340 | if (ret > 0) { |
---|
340 | 341 | if (be64_to_cpu(msg.params[1]) != php_slot->dn->phandle || |
---|
341 | | - be64_to_cpu(msg.params[2]) != state || |
---|
342 | | - be64_to_cpu(msg.params[3]) != OPAL_SUCCESS) { |
---|
343 | | - pci_warn(php_slot->pdev, "Wrong msg (%lld, %lld, %lld)\n", |
---|
344 | | - be64_to_cpu(msg.params[1]), |
---|
345 | | - be64_to_cpu(msg.params[2]), |
---|
346 | | - be64_to_cpu(msg.params[3])); |
---|
| 342 | + be64_to_cpu(msg.params[2]) != state) { |
---|
| 343 | + SLOT_WARN(php_slot, "Wrong msg (%lld, %lld, %lld)\n", |
---|
| 344 | + be64_to_cpu(msg.params[1]), |
---|
| 345 | + be64_to_cpu(msg.params[2]), |
---|
| 346 | + be64_to_cpu(msg.params[3])); |
---|
347 | 347 | return -ENOMSG; |
---|
348 | 348 | } |
---|
| 349 | + if (be64_to_cpu(msg.params[3]) != OPAL_SUCCESS) { |
---|
| 350 | + ret = -ENODEV; |
---|
| 351 | + goto error; |
---|
| 352 | + } |
---|
349 | 353 | } else if (ret < 0) { |
---|
350 | | - pci_warn(php_slot->pdev, "Error %d powering %s\n", |
---|
351 | | - ret, (state == OPAL_PCI_SLOT_POWER_ON) ? "on" : "off"); |
---|
352 | | - return ret; |
---|
| 354 | + goto error; |
---|
353 | 355 | } |
---|
354 | 356 | |
---|
355 | 357 | if (state == OPAL_PCI_SLOT_POWER_OFF || state == OPAL_PCI_SLOT_OFFLINE) |
---|
.. | .. |
---|
358 | 360 | ret = pnv_php_add_devtree(php_slot); |
---|
359 | 361 | |
---|
360 | 362 | return ret; |
---|
| 363 | + |
---|
| 364 | +error: |
---|
| 365 | + SLOT_WARN(php_slot, "Error %d powering %s\n", |
---|
| 366 | + ret, (state == OPAL_PCI_SLOT_POWER_ON) ? "on" : "off"); |
---|
| 367 | + return ret; |
---|
361 | 368 | } |
---|
362 | 369 | EXPORT_SYMBOL_GPL(pnv_php_set_slot_power_state); |
---|
363 | 370 | |
---|
364 | 371 | static int pnv_php_get_power_state(struct hotplug_slot *slot, u8 *state) |
---|
365 | 372 | { |
---|
366 | | - struct pnv_php_slot *php_slot = slot->private; |
---|
| 373 | + struct pnv_php_slot *php_slot = to_pnv_php_slot(slot); |
---|
367 | 374 | uint8_t power_state = OPAL_PCI_SLOT_POWER_ON; |
---|
368 | 375 | int ret; |
---|
369 | 376 | |
---|
.. | .. |
---|
374 | 381 | */ |
---|
375 | 382 | ret = pnv_pci_get_power_state(php_slot->id, &power_state); |
---|
376 | 383 | if (ret) { |
---|
377 | | - pci_warn(php_slot->pdev, "Error %d getting power status\n", |
---|
378 | | - ret); |
---|
| 384 | + SLOT_WARN(php_slot, "Error %d getting power status\n", |
---|
| 385 | + ret); |
---|
379 | 386 | } else { |
---|
380 | 387 | *state = power_state; |
---|
381 | | - slot->info->power_status = power_state; |
---|
382 | 388 | } |
---|
383 | 389 | |
---|
384 | 390 | return 0; |
---|
.. | .. |
---|
386 | 392 | |
---|
387 | 393 | static int pnv_php_get_adapter_state(struct hotplug_slot *slot, u8 *state) |
---|
388 | 394 | { |
---|
389 | | - struct pnv_php_slot *php_slot = slot->private; |
---|
| 395 | + struct pnv_php_slot *php_slot = to_pnv_php_slot(slot); |
---|
390 | 396 | uint8_t presence = OPAL_PCI_SLOT_EMPTY; |
---|
391 | 397 | int ret; |
---|
392 | 398 | |
---|
.. | .. |
---|
397 | 403 | ret = pnv_pci_get_presence_state(php_slot->id, &presence); |
---|
398 | 404 | if (ret >= 0) { |
---|
399 | 405 | *state = presence; |
---|
400 | | - slot->info->adapter_status = presence; |
---|
401 | 406 | ret = 0; |
---|
402 | 407 | } else { |
---|
403 | | - pci_warn(php_slot->pdev, "Error %d getting presence\n", ret); |
---|
| 408 | + SLOT_WARN(php_slot, "Error %d getting presence\n", ret); |
---|
404 | 409 | } |
---|
405 | 410 | |
---|
406 | 411 | return ret; |
---|
407 | 412 | } |
---|
408 | 413 | |
---|
| 414 | +static int pnv_php_get_attention_state(struct hotplug_slot *slot, u8 *state) |
---|
| 415 | +{ |
---|
| 416 | + struct pnv_php_slot *php_slot = to_pnv_php_slot(slot); |
---|
| 417 | + |
---|
| 418 | + *state = php_slot->attention_state; |
---|
| 419 | + return 0; |
---|
| 420 | +} |
---|
| 421 | + |
---|
409 | 422 | static int pnv_php_set_attention_state(struct hotplug_slot *slot, u8 state) |
---|
410 | 423 | { |
---|
411 | | - /* FIXME: Make it real once firmware supports it */ |
---|
412 | | - slot->info->attention_status = state; |
---|
| 424 | + struct pnv_php_slot *php_slot = to_pnv_php_slot(slot); |
---|
| 425 | + struct pci_dev *bridge = php_slot->pdev; |
---|
| 426 | + u16 new, mask; |
---|
| 427 | + |
---|
| 428 | + php_slot->attention_state = state; |
---|
| 429 | + if (!bridge) |
---|
| 430 | + return 0; |
---|
| 431 | + |
---|
| 432 | + mask = PCI_EXP_SLTCTL_AIC; |
---|
| 433 | + |
---|
| 434 | + if (state) |
---|
| 435 | + new = PCI_EXP_SLTCTL_ATTN_IND_ON; |
---|
| 436 | + else |
---|
| 437 | + new = PCI_EXP_SLTCTL_ATTN_IND_OFF; |
---|
| 438 | + |
---|
| 439 | + pcie_capability_clear_and_set_word(bridge, PCI_EXP_SLTCTL, mask, new); |
---|
413 | 440 | |
---|
414 | 441 | return 0; |
---|
415 | 442 | } |
---|
.. | .. |
---|
499 | 526 | return 0; |
---|
500 | 527 | } |
---|
501 | 528 | |
---|
| 529 | +static int pnv_php_reset_slot(struct hotplug_slot *slot, int probe) |
---|
| 530 | +{ |
---|
| 531 | + struct pnv_php_slot *php_slot = to_pnv_php_slot(slot); |
---|
| 532 | + struct pci_dev *bridge = php_slot->pdev; |
---|
| 533 | + uint16_t sts; |
---|
| 534 | + |
---|
| 535 | + /* |
---|
| 536 | + * The CAPI folks want pnv_php to drive OpenCAPI slots |
---|
| 537 | + * which don't have a bridge. Only claim to support |
---|
| 538 | + * reset_slot() if we have a bridge device (for now...) |
---|
| 539 | + */ |
---|
| 540 | + if (probe) |
---|
| 541 | + return !bridge; |
---|
| 542 | + |
---|
| 543 | + /* mask our interrupt while resetting the bridge */ |
---|
| 544 | + if (php_slot->irq > 0) |
---|
| 545 | + disable_irq(php_slot->irq); |
---|
| 546 | + |
---|
| 547 | + pci_bridge_secondary_bus_reset(bridge); |
---|
| 548 | + |
---|
| 549 | + /* clear any state changes that happened due to the reset */ |
---|
| 550 | + pcie_capability_read_word(php_slot->pdev, PCI_EXP_SLTSTA, &sts); |
---|
| 551 | + sts &= (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC); |
---|
| 552 | + pcie_capability_write_word(php_slot->pdev, PCI_EXP_SLTSTA, sts); |
---|
| 553 | + |
---|
| 554 | + if (php_slot->irq > 0) |
---|
| 555 | + enable_irq(php_slot->irq); |
---|
| 556 | + |
---|
| 557 | + return 0; |
---|
| 558 | +} |
---|
| 559 | + |
---|
502 | 560 | static int pnv_php_enable_slot(struct hotplug_slot *slot) |
---|
503 | 561 | { |
---|
504 | | - struct pnv_php_slot *php_slot = container_of(slot, |
---|
505 | | - struct pnv_php_slot, slot); |
---|
| 562 | + struct pnv_php_slot *php_slot = to_pnv_php_slot(slot); |
---|
506 | 563 | |
---|
507 | 564 | return pnv_php_enable(php_slot, true); |
---|
508 | 565 | } |
---|
509 | 566 | |
---|
510 | 567 | static int pnv_php_disable_slot(struct hotplug_slot *slot) |
---|
511 | 568 | { |
---|
512 | | - struct pnv_php_slot *php_slot = slot->private; |
---|
| 569 | + struct pnv_php_slot *php_slot = to_pnv_php_slot(slot); |
---|
513 | 570 | int ret; |
---|
514 | 571 | |
---|
515 | | - if (php_slot->state != PNV_PHP_STATE_POPULATED) |
---|
| 572 | + /* |
---|
| 573 | + * Allow to disable a slot already in the registered state to |
---|
| 574 | + * cover cases where the slot couldn't be enabled and never |
---|
| 575 | + * reached the populated state |
---|
| 576 | + */ |
---|
| 577 | + if (php_slot->state != PNV_PHP_STATE_POPULATED && |
---|
| 578 | + php_slot->state != PNV_PHP_STATE_REGISTERED) |
---|
516 | 579 | return 0; |
---|
517 | 580 | |
---|
518 | 581 | /* Remove all devices behind the slot */ |
---|
.. | .. |
---|
530 | 593 | return ret; |
---|
531 | 594 | } |
---|
532 | 595 | |
---|
533 | | -static struct hotplug_slot_ops php_slot_ops = { |
---|
| 596 | +static const struct hotplug_slot_ops php_slot_ops = { |
---|
534 | 597 | .get_power_status = pnv_php_get_power_state, |
---|
535 | 598 | .get_adapter_status = pnv_php_get_adapter_state, |
---|
| 599 | + .get_attention_status = pnv_php_get_attention_state, |
---|
536 | 600 | .set_attention_status = pnv_php_set_attention_state, |
---|
537 | 601 | .enable_slot = pnv_php_enable_slot, |
---|
538 | 602 | .disable_slot = pnv_php_disable_slot, |
---|
| 603 | + .reset_slot = pnv_php_reset_slot, |
---|
539 | 604 | }; |
---|
540 | 605 | |
---|
541 | 606 | static void pnv_php_release(struct pnv_php_slot *php_slot) |
---|
.. | .. |
---|
594 | 659 | php_slot->id = id; |
---|
595 | 660 | php_slot->power_state_check = false; |
---|
596 | 661 | php_slot->slot.ops = &php_slot_ops; |
---|
597 | | - php_slot->slot.info = &php_slot->slot_info; |
---|
598 | | - php_slot->slot.private = php_slot; |
---|
599 | 662 | |
---|
600 | 663 | INIT_LIST_HEAD(&php_slot->children); |
---|
601 | 664 | INIT_LIST_HEAD(&php_slot->link); |
---|
.. | .. |
---|
621 | 684 | ret = pci_hp_register(&php_slot->slot, php_slot->bus, |
---|
622 | 685 | php_slot->slot_no, php_slot->name); |
---|
623 | 686 | if (ret) { |
---|
624 | | - pci_warn(php_slot->pdev, "Error %d registering slot\n", ret); |
---|
| 687 | + SLOT_WARN(php_slot, "Error %d registering slot\n", ret); |
---|
625 | 688 | return ret; |
---|
626 | 689 | } |
---|
627 | 690 | |
---|
.. | .. |
---|
674 | 737 | /* Enable MSIx */ |
---|
675 | 738 | ret = pci_enable_msix_exact(pdev, &entry, 1); |
---|
676 | 739 | if (ret) { |
---|
677 | | - pci_warn(pdev, "Error %d enabling MSIx\n", ret); |
---|
| 740 | + SLOT_WARN(php_slot, "Error %d enabling MSIx\n", ret); |
---|
678 | 741 | return ret; |
---|
679 | 742 | } |
---|
680 | 743 | |
---|
.. | .. |
---|
711 | 774 | pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &sts); |
---|
712 | 775 | sts &= (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC); |
---|
713 | 776 | pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, sts); |
---|
| 777 | + |
---|
| 778 | + pci_dbg(pdev, "PCI slot [%s]: HP int! DLAct: %d, PresDet: %d\n", |
---|
| 779 | + php_slot->name, |
---|
| 780 | + !!(sts & PCI_EXP_SLTSTA_DLLSC), |
---|
| 781 | + !!(sts & PCI_EXP_SLTSTA_PDC)); |
---|
| 782 | + |
---|
714 | 783 | if (sts & PCI_EXP_SLTSTA_DLLSC) { |
---|
715 | 784 | pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lsts); |
---|
716 | 785 | added = !!(lsts & PCI_EXP_LNKSTA_DLLLA); |
---|
.. | .. |
---|
718 | 787 | (sts & PCI_EXP_SLTSTA_PDC)) { |
---|
719 | 788 | ret = pnv_pci_get_presence_state(php_slot->id, &presence); |
---|
720 | 789 | if (ret) { |
---|
721 | | - pci_warn(pdev, "PCI slot [%s] error %d getting presence (0x%04x), to retry the operation.\n", |
---|
722 | | - php_slot->name, ret, sts); |
---|
| 790 | + SLOT_WARN(php_slot, |
---|
| 791 | + "PCI slot [%s] error %d getting presence (0x%04x), to retry the operation.\n", |
---|
| 792 | + php_slot->name, ret, sts); |
---|
723 | 793 | return IRQ_HANDLED; |
---|
724 | 794 | } |
---|
725 | 795 | |
---|
726 | 796 | added = !!(presence == OPAL_PCI_SLOT_PRESENT); |
---|
727 | 797 | } else { |
---|
| 798 | + pci_dbg(pdev, "PCI slot [%s]: Spurious IRQ?\n", php_slot->name); |
---|
728 | 799 | return IRQ_NONE; |
---|
729 | 800 | } |
---|
730 | 801 | |
---|
.. | .. |
---|
736 | 807 | pe = edev ? edev->pe : NULL; |
---|
737 | 808 | if (pe) { |
---|
738 | 809 | eeh_serialize_lock(&flags); |
---|
739 | | - eeh_pe_state_mark(pe, EEH_PE_ISOLATED); |
---|
| 810 | + eeh_pe_mark_isolated(pe); |
---|
740 | 811 | eeh_serialize_unlock(flags); |
---|
741 | 812 | eeh_pe_set_option(pe, EEH_OPT_FREEZE_PE); |
---|
742 | 813 | } |
---|
.. | .. |
---|
748 | 819 | */ |
---|
749 | 820 | event = kzalloc(sizeof(*event), GFP_ATOMIC); |
---|
750 | 821 | if (!event) { |
---|
751 | | - pci_warn(pdev, "PCI slot [%s] missed hotplug event 0x%04x\n", |
---|
752 | | - php_slot->name, sts); |
---|
| 822 | + SLOT_WARN(php_slot, |
---|
| 823 | + "PCI slot [%s] missed hotplug event 0x%04x\n", |
---|
| 824 | + php_slot->name, sts); |
---|
753 | 825 | return IRQ_HANDLED; |
---|
754 | 826 | } |
---|
755 | 827 | |
---|
.. | .. |
---|
773 | 845 | /* Allocate workqueue */ |
---|
774 | 846 | php_slot->wq = alloc_workqueue("pciehp-%s", 0, 0, php_slot->name); |
---|
775 | 847 | if (!php_slot->wq) { |
---|
776 | | - pci_warn(pdev, "Cannot alloc workqueue\n"); |
---|
| 848 | + SLOT_WARN(php_slot, "Cannot alloc workqueue\n"); |
---|
777 | 849 | pnv_php_disable_irq(php_slot, true); |
---|
778 | 850 | return; |
---|
779 | 851 | } |
---|
.. | .. |
---|
797 | 869 | php_slot->name, php_slot); |
---|
798 | 870 | if (ret) { |
---|
799 | 871 | pnv_php_disable_irq(php_slot, true); |
---|
800 | | - pci_warn(pdev, "Error %d enabling IRQ %d\n", ret, irq); |
---|
| 872 | + SLOT_WARN(php_slot, "Error %d enabling IRQ %d\n", ret, irq); |
---|
801 | 873 | return; |
---|
802 | 874 | } |
---|
803 | 875 | |
---|
.. | .. |
---|
833 | 905 | |
---|
834 | 906 | ret = pci_enable_device(pdev); |
---|
835 | 907 | if (ret) { |
---|
836 | | - pci_warn(pdev, "Error %d enabling device\n", ret); |
---|
| 908 | + SLOT_WARN(php_slot, "Error %d enabling device\n", ret); |
---|
837 | 909 | return; |
---|
838 | 910 | } |
---|
839 | 911 | |
---|
.. | .. |
---|
945 | 1017 | for_each_compatible_node(dn, NULL, "ibm,ioda2-phb") |
---|
946 | 1018 | pnv_php_register(dn); |
---|
947 | 1019 | |
---|
| 1020 | + for_each_compatible_node(dn, NULL, "ibm,ioda3-phb") |
---|
| 1021 | + pnv_php_register(dn); |
---|
| 1022 | + |
---|
| 1023 | + for_each_compatible_node(dn, NULL, "ibm,ioda2-npu2-opencapi-phb") |
---|
| 1024 | + pnv_php_register_one(dn); /* slot directly under the PHB */ |
---|
948 | 1025 | return 0; |
---|
949 | 1026 | } |
---|
950 | 1027 | |
---|
.. | .. |
---|
954 | 1031 | |
---|
955 | 1032 | for_each_compatible_node(dn, NULL, "ibm,ioda2-phb") |
---|
956 | 1033 | pnv_php_unregister(dn); |
---|
| 1034 | + |
---|
| 1035 | + for_each_compatible_node(dn, NULL, "ibm,ioda3-phb") |
---|
| 1036 | + pnv_php_unregister(dn); |
---|
| 1037 | + |
---|
| 1038 | + for_each_compatible_node(dn, NULL, "ibm,ioda2-npu2-opencapi-phb") |
---|
| 1039 | + pnv_php_unregister_one(dn); /* slot directly under the PHB */ |
---|
957 | 1040 | } |
---|
958 | 1041 | |
---|
959 | 1042 | module_init(pnv_php_init); |
---|