📄 eeh.c
字号:
PCI_DN(dn)->eeh_check_count = 0; if (dn->child) __eeh_clear_slot (dn->child, mode_flag); } dn = dn->sibling; }}void eeh_clear_slot (struct device_node *dn, int mode_flag){ unsigned long flags; spin_lock_irqsave(&confirm_error_lock, flags); dn = find_device_pe (dn); /* Back up one, since config addrs might be shared */ if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent)) dn = dn->parent; PCI_DN(dn)->eeh_mode &= ~mode_flag; PCI_DN(dn)->eeh_check_count = 0; __eeh_clear_slot (dn->child, mode_flag); spin_unlock_irqrestore(&confirm_error_lock, flags);}/** * eeh_dn_check_failure - check if all 1's data is due to EEH slot freeze * @dn device node * @dev pci device, if known * * Check for an EEH failure for the given device node. Call this * routine if the result of a read was all 0xff's and you want to * find out if this is due to an EEH slot freeze. This routine * will query firmware for the EEH status. * * Returns 0 if there has not been an EEH error; otherwise returns * a non-zero value and queues up a slot isolation event notification. * * It is safe to call this routine in an interrupt context. */int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev){ int ret; int rets[3]; unsigned long flags; struct pci_dn *pdn; int rc = 0; total_mmio_ffs++; if (!eeh_subsystem_enabled) return 0; if (!dn) { no_dn++; return 0; } pdn = PCI_DN(dn); /* Access to IO BARs might get this far and still not want checking. */ if (!(pdn->eeh_mode & EEH_MODE_SUPPORTED) || pdn->eeh_mode & EEH_MODE_NOCHECK) { ignored_check++;#ifdef DEBUG printk ("EEH:ignored check (%x) for %s %s\n", pdn->eeh_mode, pci_name (dev), dn->full_name);#endif return 0; } if (!pdn->eeh_config_addr && !pdn->eeh_pe_config_addr) { no_cfg_addr++; return 0; } /* If we already have a pending isolation event for this * slot, we know it's bad already, we don't need to check. * Do this checking under a lock; as multiple PCI devices * in one slot might report errors simultaneously, and we * only want one error recovery routine running. */ spin_lock_irqsave(&confirm_error_lock, flags); rc = 1; if (pdn->eeh_mode & EEH_MODE_ISOLATED) { pdn->eeh_check_count ++; if (pdn->eeh_check_count >= EEH_MAX_FAILS) { printk (KERN_ERR "EEH: Device driver ignored %d bad reads, panicing\n", pdn->eeh_check_count); dump_stack(); msleep(5000); /* re-read the slot reset state */ if (read_slot_reset_state(pdn, rets) != 0) rets[0] = -1; /* reset state unknown */ /* If we are here, then we hit an infinite loop. Stop. */ panic("EEH: MMIO halt (%d) on device:%s\n", rets[0], pci_name(dev)); } goto dn_unlock; } /* * Now test for an EEH failure. This is VERY expensive. * Note that the eeh_config_addr may be a parent device * in the case of a device behind a bridge, or it may be * function zero of a multi-function device. * In any case they must share a common PHB. */ ret = read_slot_reset_state(pdn, rets); /* If the call to firmware failed, punt */ if (ret != 0) { printk(KERN_WARNING "EEH: read_slot_reset_state() failed; rc=%d dn=%s\n", ret, dn->full_name); false_positives++; pdn->eeh_false_positives ++; rc = 0; goto dn_unlock; } /* Note that config-io to empty slots may fail; * they are empty when they don't have children. */ if ((rets[0] == 5) && (dn->child == NULL)) { false_positives++; pdn->eeh_false_positives ++; rc = 0; goto dn_unlock; } /* If EEH is not supported on this device, punt. */ if (rets[1] != 1) { printk(KERN_WARNING "EEH: event on unsupported device, rc=%d dn=%s\n", ret, dn->full_name); false_positives++; pdn->eeh_false_positives ++; rc = 0; goto dn_unlock; } /* If not the kind of error we know about, punt. */ if (rets[0] != 1 && rets[0] != 2 && rets[0] != 4 && rets[0] != 5) { false_positives++; pdn->eeh_false_positives ++; rc = 0; goto dn_unlock; } slot_resets++; /* Avoid repeated reports of this failure, including problems * with other functions on this device, and functions under * bridges. */ eeh_mark_slot (dn, EEH_MODE_ISOLATED); spin_unlock_irqrestore(&confirm_error_lock, flags); eeh_send_failure_event (dn, dev); /* Most EEH events are due to device driver bugs. Having * a stack trace will help the device-driver authors figure * out what happened. So print that out. */ dump_stack(); return 1;dn_unlock: spin_unlock_irqrestore(&confirm_error_lock, flags); return rc;}EXPORT_SYMBOL_GPL(eeh_dn_check_failure);/** * eeh_check_failure - check if all 1's data is due to EEH slot freeze * @token i/o token, should be address in the form 0xA.... * @val value, should be all 1's (XXX why do we need this arg??) * * Check for an EEH failure at the given token address. Call this * routine if the result of a read was all 0xff's and you want to * find out if this is due to an EEH slot freeze event. This routine * will query firmware for the EEH status. * * Note this routine is safe to call in an interrupt context. */unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned long val){ unsigned long addr; struct pci_dev *dev; struct device_node *dn; /* Finding the phys addr + pci device; this is pretty quick. */ addr = eeh_token_to_phys((unsigned long __force) token); dev = pci_get_device_by_addr(addr); if (!dev) { no_device++; return val; } dn = pci_device_to_OF_node(dev); eeh_dn_check_failure (dn, dev); pci_dev_put(dev); return val;}EXPORT_SYMBOL(eeh_check_failure);/* ------------------------------------------------------------- *//* The code below deals with error recovery *//** * rtas_pci_enable - enable MMIO or DMA transfers for this slot * @pdn pci device node */intrtas_pci_enable(struct pci_dn *pdn, int function){ int config_addr; int rc; /* Use PE configuration address, if present */ config_addr = pdn->eeh_config_addr; if (pdn->eeh_pe_config_addr) config_addr = pdn->eeh_pe_config_addr; rc = rtas_call(ibm_set_eeh_option, 4, 1, NULL, config_addr, BUID_HI(pdn->phb->buid), BUID_LO(pdn->phb->buid), function); if (rc) printk(KERN_WARNING "EEH: Unexpected state change %d, err=%d dn=%s\n", function, rc, pdn->node->full_name); rc = eeh_wait_for_slot_status (pdn, PCI_BUS_RESET_WAIT_MSEC); if ((rc == 4) && (function == EEH_THAW_MMIO)) return 0; return rc;}/** * rtas_pci_slot_reset - raises/lowers the pci #RST line * @pdn pci device node * @state: 1/0 to raise/lower the #RST * * Clear the EEH-frozen condition on a slot. This routine * asserts the PCI #RST line if the 'state' argument is '1', * and drops the #RST line if 'state is '0'. This routine is * safe to call in an interrupt context. * */static voidrtas_pci_slot_reset(struct pci_dn *pdn, int state){ int config_addr; int rc; BUG_ON (pdn==NULL); if (!pdn->phb) { printk (KERN_WARNING "EEH: in slot reset, device node %s has no phb\n", pdn->node->full_name); return; } /* Use PE configuration address, if present */ config_addr = pdn->eeh_config_addr; if (pdn->eeh_pe_config_addr) config_addr = pdn->eeh_pe_config_addr; rc = rtas_call(ibm_set_slot_reset,4,1, NULL, config_addr, BUID_HI(pdn->phb->buid), BUID_LO(pdn->phb->buid), state); if (rc) printk (KERN_WARNING "EEH: Unable to reset the failed slot," " (%d) #RST=%d dn=%s\n", rc, state, pdn->node->full_name);}/** * pcibios_set_pcie_slot_reset - Set PCI-E reset state * @dev: pci device struct * @state: reset state to enter * * Return value: * 0 if success **/int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state){ struct device_node *dn = pci_device_to_OF_node(dev); struct pci_dn *pdn = PCI_DN(dn); switch (state) { case pcie_deassert_reset: rtas_pci_slot_reset(pdn, 0); break; case pcie_hot_reset: rtas_pci_slot_reset(pdn, 1); break; case pcie_warm_reset: rtas_pci_slot_reset(pdn, 3); break; default: return -EINVAL; }; return 0;}/** * rtas_set_slot_reset -- assert the pci #RST line for 1/4 second * @pdn: pci device node to be reset. * * Return 0 if success, else a non-zero value. */static void __rtas_set_slot_reset(struct pci_dn *pdn){ rtas_pci_slot_reset (pdn, 1); /* The PCI bus requires that the reset be held high for at least * a 100 milliseconds. We wait a bit longer 'just in case'. */#define PCI_BUS_RST_HOLD_TIME_MSEC 250 msleep (PCI_BUS_RST_HOLD_TIME_MSEC); /* We might get hit with another EEH freeze as soon as the * pci slot reset line is dropped. Make sure we don't miss * these, and clear the flag now. */ eeh_clear_slot (pdn->node, EEH_MODE_ISOLATED); rtas_pci_slot_reset (pdn, 0); /* After a PCI slot has been reset, the PCI Express spec requires * a 1.5 second idle time for the bus to stabilize, before starting * up traffic. */#define PCI_BUS_SETTLE_TIME_MSEC 1800 msleep (PCI_BUS_SETTLE_TIME_MSEC);}int rtas_set_slot_reset(struct pci_dn *pdn){ int i, rc; /* Take three shots at resetting the bus */ for (i=0; i<3; i++) { __rtas_set_slot_reset(pdn); rc = eeh_wait_for_slot_status(pdn, PCI_BUS_RESET_WAIT_MSEC); if (rc == 0) return 0; if (rc < 0) { printk(KERN_ERR "EEH: unrecoverable slot failure %s\n", pdn->node->full_name); return -1; } printk(KERN_ERR "EEH: bus reset %d failed on slot %s, rc=%d\n", i+1, pdn->node->full_name, rc); } return -1;}/* ------------------------------------------------------- *//** Save and restore of PCI BARs * * Although firmware will set up BARs during boot, it doesn't * set up device BAR's after a device reset, although it will, * if requested, set up bridge configuration. Thus, we need to * configure the PCI devices ourselves. *//** * __restore_bars - Restore the Base Address Registers * @pdn: pci device node * * Loads the PCI configuration space base address registers, * the expansion ROM base address, the latency timer, and etc. * from the saved values in the device node. */static inline void __restore_bars (struct pci_dn *pdn){ int i; if (NULL==pdn->phb) return; for (i=4; i<10; i++) { rtas_write_config(pdn, i*4, 4, pdn->config_space[i]); } /* 12 == Expansion ROM Address */ rtas_write_config(pdn, 12*4, 4, pdn->config_space[12]);#define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF))#define SAVED_BYTE(OFF) (((u8 *)(pdn->config_space))[BYTE_SWAP(OFF)]) rtas_write_config (pdn, PCI_CACHE_LINE_SIZE, 1, SAVED_BYTE(PCI_CACHE_LINE_SIZE)); rtas_write_config (pdn, PCI_LATENCY_TIMER, 1, SAVED_BYTE(PCI_LATENCY_TIMER)); /* max latency, min grant, interrupt pin and line */ rtas_write_config(pdn, 15*4, 4, pdn->config_space[15]);}/** * eeh_restore_bars - restore the PCI config space info * * This routine performs a recursive walk to the children * of this device as well. */void eeh_restore_bars(struct pci_dn *pdn){ struct device_node *dn; if (!pdn) return; if ((pdn->eeh_mode & EEH_MODE_SUPPORTED) && !IS_BRIDGE(pdn->class_code)) __restore_bars (pdn);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -