/* * Remove pdn for all children of the indicated device node. * The function should remove pdn in a depth-first manner.
*/ staticvoid pnv_php_rmv_pdns(struct device_node *dn)
{ struct device_node *child;
/* * Detach all child nodes of the indicated device nodes. The * function should handle device nodes in depth-first manner. * * We should not invoke of_node_release() as the memory for * individual device node is part of large memory block. The * large block is allocated from memblock (system bootup) or * kmalloc() when unflattening the device tree by OF changeset. * We can not free the large block allocated from memblock. For * later case, it should be released at once.
*/ staticvoid pnv_php_detach_device_nodes(struct device_node *parent)
{ struct device_node *dn;
/* * Decrease the refcount if the device nodes were created * through OF changeset before detaching them.
*/ if (php_slot->fdt)
of_changeset_destroy(&php_slot->ocs);
pnv_php_detach_device_nodes(php_slot->dn);
/* * As the nodes in OF changeset are applied in reverse order, we * need revert the nodes in advance so that we have correct node * order after the changeset is applied.
*/ staticvoid pnv_php_reverse_nodes(struct device_node *parent)
{ struct device_node *child, *next;
/* In-depth first */
for_each_child_of_node(parent, child)
pnv_php_reverse_nodes(child);
/* Reverse the nodes in the child list */
child = parent->child;
parent->child = NULL; while (child) {
next = child->sibling;
/* We don't know the FDT blob size. We try to get it through * maximal memory chunk and then copy it to another chunk that * fits the real size.
*/
fdt1 = kzalloc(0x10000, GFP_KERNEL); if (!fdt1) {
ret = -ENOMEM; goto out;
}
ret = pnv_pci_get_device_tree(php_slot->dn->phandle, fdt1, 0x10000); if (ret) {
SLOT_WARN(php_slot, "Error %d getting FDT blob\n", ret); goto free_fdt1;
}
fdt = kmemdup(fdt1, fdt_totalsize(fdt1), GFP_KERNEL); if (!fdt) {
ret = -ENOMEM; goto free_fdt1;
}
/* Unflatten device tree blob */
dt = of_fdt_unflatten_tree(fdt, php_slot->dn, NULL); if (!dt) {
ret = -EINVAL;
SLOT_WARN(php_slot, "Cannot unflatten FDT\n"); goto free_fdt;
}
/* Initialize and apply the changeset */
of_changeset_init(&php_slot->ocs);
pnv_php_reverse_nodes(php_slot->dn);
ret = pnv_php_populate_changeset(&php_slot->ocs, php_slot->dn); if (ret) {
pnv_php_reverse_nodes(php_slot->dn);
SLOT_WARN(php_slot, "Error %d populating changeset\n",
ret); goto free_dt;
}
php_slot->dn->child = NULL;
ret = of_changeset_apply(&php_slot->ocs); if (ret) {
SLOT_WARN(php_slot, "Error %d applying changeset\n", ret); goto destroy_changeset;
}
/* * Retrieve power status from firmware. If we fail * getting that, the power status fails back to * be on.
*/
ret = pnv_pci_get_power_state(php_slot->id, &power_state); if (ret) {
SLOT_WARN(php_slot, "Error %d getting power status\n",
ret);
} else {
*state = power_state;
}
return 0;
}
staticint pcie_check_link_active(struct pci_dev *pdev)
{
u16 lnk_status; int ret;
ret = pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status); if (ret == PCIBIOS_DEVICE_NOT_FOUND || PCI_POSSIBLE_ERROR(lnk_status)) return -ENODEV;
/* * Retrieve presence status from firmware. If we can't * get that, it will fail back to be empty.
*/
ret = pnv_pci_get_presence_state(php_slot->id, &presence); if (ret >= 0) { if (pci_pcie_type(php_slot->pdev) == PCI_EXP_TYPE_DOWNSTREAM &&
presence == OPAL_PCI_SLOT_EMPTY) { /* * Similar to pciehp_hpc, check whether the Link Active * bit is set to account for broken downstream bridges * that don't properly assert Presence Detect State, as * was observed on the Microsemi Switchtec PM8533 PFX * [11f8:8533].
*/ if (pcie_check_link_active(php_slot->pdev) > 0)
presence = OPAL_PCI_SLOT_PRESENT;
}
/* * Issue initial slot activation command to firmware * * Firmware will power slot on, attempt to train the link, and * discover any downstream devices. If this process fails, firmware * will return an error code and an invalid device tree. Failure * can be caused for multiple reasons, including a faulty * downstream device, poor connection to the downstream device, or * a previously latched PHB fence. On failure, issue fundamental * reset up to three times before aborting.
*/
ret = pnv_php_set_slot_power_state(slot, OPAL_PCI_SLOT_POWER_ON); if (ret) {
SLOT_WARN(
php_slot, "PCI slot activation failed with error code %d, possible frozen PHB",
ret);
SLOT_WARN(
php_slot, "Attempting complete PHB reset before retrying slot activation\n"); for (i = 0; i < 3; i++) { /* * Slot activation failed, PHB may be fenced from a * prior device failure. * * Use the OPAL fundamental reset call to both try a * device reset and clear any potentially active PHB * fence / freeze.
*/
SLOT_WARN(php_slot, "Try %d...\n", i + 1);
pci_set_pcie_reset_state(php_slot->pdev,
pcie_warm_reset);
msleep(250);
pci_set_pcie_reset_state(php_slot->pdev,
pcie_deassert_reset);
ret = pnv_php_set_slot_power_state(
slot, OPAL_PCI_SLOT_POWER_ON); if (!ret) break;
}
if (i >= 3)
SLOT_WARN(php_slot, "Failed to bring slot online, aborting!\n");
}
/* Check if the slot has been configured */ if (php_slot->state != PNV_PHP_STATE_REGISTERED) return 0;
/* Retrieve slot presence status */
ret = pnv_php_get_adapter_state(slot, &presence); if (ret) return ret;
/* * Proceed if there have nothing behind the slot. However, * we should leave the slot in registered state at the * beginning. Otherwise, the PCI devices inserted afterwards * won't be probed and populated.
*/ if (presence == OPAL_PCI_SLOT_EMPTY) { if (!php_slot->power_state_check) {
php_slot->power_state_check = true;
return 0;
}
goto scan;
}
/* * If the power supply to the slot is off, we can't detect * adapter presence state. That means we have to turn the * slot on before going to probe slot's presence state. * * On the first time, we don't change the power status to * boost system boot with assumption that the firmware * supplies consistent slot power status: empty slot always * has its power off and non-empty slot has its power on.
*/ if (!php_slot->power_state_check) {
php_slot->power_state_check = true;
ret = pnv_php_get_power_state(slot, &power_status); if (ret) return ret;
if (power_status != OPAL_PCI_SLOT_POWER_ON) return 0;
}
/* Check the power status. Scan the slot if it is already on */
ret = pnv_php_get_power_state(slot, &power_status); if (ret) return ret;
if (power_status == OPAL_PCI_SLOT_POWER_ON) goto scan;
/* Power is off, turn it on and then scan the slot */
ret = pnv_php_activate_slot(php_slot, slot); if (ret) return ret;
scan: if (presence == OPAL_PCI_SLOT_PRESENT) { if (rescan) {
pci_lock_rescan_remove();
pci_hp_add_devices(php_slot->bus);
pci_unlock_rescan_remove();
}
/* Rescan for child hotpluggable slots */
php_slot->state = PNV_PHP_STATE_POPULATED; if (rescan)
pnv_php_register(php_slot->dn);
} else {
php_slot->state = PNV_PHP_STATE_POPULATED;
}
/* * The CAPI folks want pnv_php to drive OpenCAPI slots * which don't have a bridge. Only claim to support * reset_slot() if we have a bridge device (for now...)
*/ if (probe) return !bridge;
/* mask our interrupt while resetting the bridge */ if (php_slot->irq > 0)
disable_irq(php_slot->irq);
pci_bridge_secondary_bus_reset(bridge);
/* clear any state changes that happened due to the reset */
pcie_capability_read_word(php_slot->pdev, PCI_EXP_SLTSTA, &sts);
sts &= (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC);
pcie_capability_write_word(php_slot->pdev, PCI_EXP_SLTSTA, sts);
ret = pnv_php_enable(php_slot, true); if (ret) return ret;
/* (Re-)enable interrupt if the slot supports surprise hotplug */
ret = of_property_read_u32(php_slot->dn, "ibm,slot-surprise-pluggable",
&prop32); if (!ret && prop32)
pnv_php_enable_irq(php_slot);
return 0;
}
/* * Disable any hotplug interrupts for all slots on the provided bus, as well as * all downstream slots in preparation for a hot unplug.
*/ staticint pnv_php_disable_all_irqs(struct pci_bus *bus)
{ struct pci_bus *child_bus; struct pci_slot *slot;
/* First go down child buses */
list_for_each_entry(child_bus, &bus->children, node)
pnv_php_disable_all_irqs(child_bus);
/* Disable IRQs for all pnv_php slots on this bus */
list_for_each_entry(slot, &bus->slots, list) { struct pnv_php_slot *php_slot = to_pnv_php_slot(slot->hotplug);
pnv_php_disable_irq(php_slot, false, true);
}
return 0;
}
/* * Disable any hotplug interrupts for all downstream slots on the provided * bus in preparation for a hot unplug.
*/ staticint pnv_php_disable_all_downstream_irqs(struct pci_bus *bus)
{ struct pci_bus *child_bus;
/* Go down child buses, recursively deactivating their IRQs */
list_for_each_entry(child_bus, &bus->children, node)
pnv_php_disable_all_irqs(child_bus);
/* * Allow to disable a slot already in the registered state to * cover cases where the slot couldn't be enabled and never * reached the populated state
*/ if (php_slot->state != PNV_PHP_STATE_POPULATED &&
php_slot->state != PNV_PHP_STATE_REGISTERED) return 0;
/* * Free all IRQ resources from all child slots before remove. * Note that we do not disable the root slot IRQ here as that * would also deactivate the slot hot (re)plug interrupt!
*/
pnv_php_disable_all_downstream_irqs(php_slot->bus);
/* Remove all devices behind the slot */
pci_lock_rescan_remove();
pci_hp_remove_devices(php_slot->bus);
pci_unlock_rescan_remove();
/* Detach the child hotpluggable slots */
pnv_php_unregister(php_slot->dn);
/* Notify firmware and remove device nodes */
ret = pnv_php_set_slot_power_state(slot, OPAL_PCI_SLOT_POWER_OFF);
/* Remove from global or child list */
spin_lock_irqsave(&pnv_php_lock, flags);
list_del(&php_slot->link);
spin_unlock_irqrestore(&pnv_php_lock, flags);
/* Detach from parent */
pnv_php_put_slot(php_slot);
pnv_php_put_slot(php_slot->parent);
}
staticvoid
pnv_php_detect_clear_suprise_removal_freeze(struct pnv_php_slot *php_slot)
{ struct pci_dev *pdev = php_slot->pdev; struct eeh_dev *edev; struct eeh_pe *pe; int i, rc;
/* * When a device is surprise removed from a downstream bridge slot, * the upstream bridge port can still end up frozen due to related EEH * events, which will in turn block the MSI interrupts for slot hotplug * detection. * * Detect and thaw any frozen upstream PE after slot deactivation.
*/
edev = pci_dev_to_eeh_dev(pdev);
pe = edev ? edev->pe : NULL;
rc = eeh_pe_get_state(pe); if ((rc == -ENODEV) || (rc == -ENOENT)) {
SLOT_WARN(
php_slot, "Upstream bridge PE state unknown, hotplug detect may fail\n");
} else { if (pe->state & EEH_PE_ISOLATED) {
SLOT_WARN(
php_slot, "Upstream bridge PE %02x frozen, thawing...\n",
pe->addr); for (i = 0; i < 3; i++) if (!eeh_unfreeze_pe(pe)) break; if (i >= 3)
SLOT_WARN(
php_slot, "Unable to thaw PE %02x, hotplug detect will fail!\n",
pe->addr); else
SLOT_WARN(php_slot, "PE %02x thawed successfully\n",
pe->addr);
}
}
}
/* Freeze the removed PE to avoid unexpected error reporting */ if (!added) {
pchild = list_first_entry_or_null(&php_slot->bus->devices, struct pci_dev, bus_list);
edev = pchild ? pci_dev_to_eeh_dev(pchild) : NULL;
pe = edev ? edev->pe : NULL; if (pe) {
eeh_serialize_lock(&flags);
eeh_pe_mark_isolated(pe);
eeh_serialize_unlock(flags);
eeh_pe_set_option(pe, EEH_OPT_FREEZE_PE);
}
}
/* * The PE is left in frozen state if the event is missed. It's * fine as the PCI devices (PE) aren't functional any more.
*/
event = kzalloc(sizeof(*event), GFP_ATOMIC); if (!event) {
SLOT_WARN(php_slot, "PCI slot [%s] missed hotplug event 0x%04x\n",
php_slot->name, sts); return IRQ_HANDLED;
}
/* Check PDC (Presence Detection Change) is broken or not */
ret = of_property_read_u32(php_slot->dn, "ibm,slot-broken-pdc",
&broken_pdc); if (!ret && broken_pdc)
php_slot->flags |= PNV_PHP_FLAG_BROKEN_PDC;
/* * The MSI/MSIx interrupt might have been occupied by other * drivers. Don't populate the surprise hotplug capability * in that case.
*/ if (pci_dev_msi_enabled(pdev)) return;
ret = pci_enable_device(pdev); if (ret) {
SLOT_WARN(php_slot, "Error %d enabling device\n", ret); return;
}
/* * Use MSI if MSIx doesn't work. Fail back to legacy INTx * if MSI doesn't work either
*/
ret = pci_enable_msi(pdev); if (!ret || pdev->irq) {
irq = pdev->irq;
pnv_php_init_irq(php_slot, irq);
}
}
/* Check if it's hotpluggable slot */
ret = of_property_read_u32(dn, "ibm,slot-pluggable", &prop32); if (ret || !prop32) return -ENXIO;
ret = of_property_read_u32(dn, "ibm,reset-by-firmware", &prop32); if (ret || !prop32) return -ENXIO;
php_slot = pnv_php_alloc_slot(dn); if (!php_slot) return -ENODEV;
ret = pnv_php_register_slot(php_slot); if (ret) goto free_slot;
ret = pnv_php_enable(php_slot, false); if (ret) goto unregister_slot;
/* Enable interrupt if the slot supports surprise hotplug */
ret = of_property_read_u32(dn, "ibm,slot-surprise-pluggable", &prop32); if (!ret && prop32)
pnv_php_enable_irq(php_slot);
/* * The parent slots should be registered before their * child slots.
*/
for_each_child_of_node(dn, child) {
pnv_php_register_one(child);
pnv_php_register(child);
}
}
/* The child slots should go before their parent slots */
for_each_child_of_node(dn, child) {
pnv_php_unregister(child);
pnv_php_unregister_one(child);
}
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.14Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.