Beispiel #1
0
/**
 * handle_error_source - handle logging error into an event log
 * @aerdev: pointer to pcie_device data structure of the root port
 * @dev: pointer to pci_dev data structure of error source device
 * @info: comprehensive error information
 *
 * Invoked when an error being detected by Root Port.
 **/
static void handle_error_source(struct pcie_device * aerdev,
	struct pci_dev *dev,
	struct aer_err_info info)
{
	pci_ers_result_t status = 0;
	int pos;

	if (info.severity == AER_CORRECTABLE) {
		/*
		 * Correctable error does not need software intevention.
		 * No need to go through error recovery process.
		 */
		pos = pci_find_aer_capability(dev);
		if (pos)
			pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS,
					info.status);
	} else {
		status = do_recovery(aerdev, dev, info.severity);
		if (status == PCI_ERS_RESULT_RECOVERED) {
			printk(KERN_DEBUG "AER driver successfully recovered\n");
		} else {
			/* TODO: Should kernel panic here? */
			printk(KERN_DEBUG "AER driver didn't recover\n");
		}
	}
}
Beispiel #2
0
/**
 * disable_root_aer - disable Root Port's interrupts when receiving messages
 * @rpc: pointer to a Root Port data structure
 *
 * Invoked when PCIE bus unloads AER service driver.
 **/
static void disable_root_aer(struct aer_rpc *rpc)
{
	struct pci_dev *pdev = rpc->rpd->port;
	u32 reg32;
	int pos;

	pos = pci_find_aer_capability(pdev);
	/* Disable Root's interrupt in response to error messages */
	pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, 0);

	/* Clear Root's error status reg */
	pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, &reg32);
	pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, reg32);
}
Beispiel #3
0
int pci_cleanup_aer_correct_error_status(struct pci_dev *dev)
{
	int pos;
	u32 status;

	pos = pci_find_aer_capability(dev);
	if (!pos)
		return -EIO;

	pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &status);
	pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, status);

	return 0;
}
Beispiel #4
0
/**
 * aer_irq - Root Port's ISR
 * @irq: IRQ assigned to Root Port
 * @context: pointer to Root Port data structure
 *
 * Invoked when Root Port detects AER messages.
 **/
static irqreturn_t aer_irq(int irq, void *context)
{
    unsigned int status, id;
    struct pcie_device *pdev = (struct pcie_device *)context;
    struct aer_rpc *rpc = get_service_data(pdev);
    int next_prod_idx;
    unsigned long flags;
    int pos;

    pos = pci_find_aer_capability(pdev->port);
    /*
     * Must lock access to Root Error Status Reg, Root Error ID Reg,
     * and Root error producer/consumer index
     */
    spin_lock_irqsave(&rpc->e_lock, flags);

    /* Read error status */
    pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, &status);
    if (!(status & ROOT_ERR_STATUS_MASKS)) {
        spin_unlock_irqrestore(&rpc->e_lock, flags);
        return IRQ_NONE;
    }

    /* Read error source and clear error status */
    pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_COR_SRC, &id);
    pci_write_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, status);

    /* Store error source for later DPC handler */
    next_prod_idx = rpc->prod_idx + 1;
    if (next_prod_idx == AER_ERROR_SOURCES_MAX)
        next_prod_idx = 0;
    if (next_prod_idx == rpc->cons_idx) {
        /*
         * Error Storm Condition - possibly the same error occurred.
         * Drop the error.
         */
        spin_unlock_irqrestore(&rpc->e_lock, flags);
        return IRQ_HANDLED;
    }
    rpc->e_sources[rpc->prod_idx].status =  status;
    rpc->e_sources[rpc->prod_idx].id = id;
    rpc->prod_idx = next_prod_idx;
    spin_unlock_irqrestore(&rpc->e_lock, flags);

    /*  Invoke DPC handler */
    schedule_work(&rpc->dpc_handler);

    return IRQ_HANDLED;
}
Beispiel #5
0
int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev)
{
	int pos;
	u32 status, mask;

	pos = pci_find_aer_capability(dev);
	if (!pos)
		return -EIO;

	pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
	pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask);
	if (dev->error_state == pci_channel_io_normal)
		status &= ~mask; /* Clear corresponding nonfatal bits */
	else
		status &= mask; /* Clear corresponding fatal bits */
	pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status);

	return 0;
}
Beispiel #6
0
/**
 * aer_enable_rootport - enable Root Port's interrupts when receiving messages
 * @rpc: pointer to a Root Port data structure
 *
 * Invoked when PCIE bus loads AER service driver.
 **/
void aer_enable_rootport(struct aer_rpc *rpc)
{
	struct pci_dev *pdev = rpc->rpd->port;
	int pos, aer_pos;
	u16 reg16;
	u32 reg32;

	pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
	/* Clear PCIE Capability's Device Status */
	pci_read_config_word(pdev, pos+PCI_EXP_DEVSTA, &reg16);
	pci_write_config_word(pdev, pos+PCI_EXP_DEVSTA, reg16);

	/* Disable system error generation in response to error messages */
	pci_read_config_word(pdev, pos + PCI_EXP_RTCTL, &reg16);
	reg16 &= ~(SYSTEM_ERROR_INTR_ON_MESG_MASK);
	pci_write_config_word(pdev, pos + PCI_EXP_RTCTL, reg16);

	aer_pos = pci_find_aer_capability(pdev);
	/* Clear error status */
	pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, &reg32);
	pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, reg32);
	pci_read_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, &reg32);
	pci_write_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, reg32);
	pci_read_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, &reg32);
	pci_write_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, reg32);

	/* Enable Root Port device reporting error itself */
	pci_read_config_word(pdev, pos+PCI_EXP_DEVCTL, &reg16);
	reg16 = reg16 |
		PCI_EXP_DEVCTL_CERE |
		PCI_EXP_DEVCTL_NFERE |
		PCI_EXP_DEVCTL_FERE |
		PCI_EXP_DEVCTL_URRE;
	pci_write_config_word(pdev, pos+PCI_EXP_DEVCTL,
		reg16);

	/* Enable Root Port's interrupt in response to error messages */
	pci_write_config_dword(pdev,
		aer_pos + PCI_ERR_ROOT_COMMAND,
		ROOT_PORT_INTR_ON_MESG_MASK);
}
Beispiel #7
0
/**
 * aer_error_resume - clean up corresponding error status bits
 * @dev: pointer to Root Port's pci_dev data structure
 *
 * Invoked by Port Bus driver during nonfatal recovery.
 **/
static void aer_error_resume(struct pci_dev *dev)
{
    int pos;
    u32 status, mask;
    u16 reg16;

    /* Clean up Root device status */
    pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
    pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, &reg16);
    pci_write_config_word(dev, pos + PCI_EXP_DEVSTA, reg16);

    /* Clean AER Root Error Status */
    pos = pci_find_aer_capability(dev);
    pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
    pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask);
    if (dev->error_state == pci_channel_io_normal)
        status &= ~mask; /* Clear corresponding nonfatal bits */
    else
        status &= mask; /* Clear corresponding fatal bits */
    pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status);
}
Beispiel #8
0
static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)
{
	int pos;

	pos = pci_find_aer_capability(dev);

	/* The device might not support AER */
	if (!pos)
		return AER_SUCCESS;

	if (info->severity == AER_CORRECTABLE) {
		pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS,
			&info->status);
		if (!(info->status & ERR_CORRECTABLE_ERROR_MASK))
			return AER_UNSUCCESS;
	} else if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE ||
		info->severity == AER_NONFATAL) {

		/* Link is still healthy for IO reads */
		pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS,
			&info->status);
		if (!(info->status & ERR_UNCORRECTABLE_ERROR_MASK))
			return AER_UNSUCCESS;

		if (info->status & AER_LOG_TLP_MASKS) {
			info->flags |= AER_TLP_HEADER_VALID_FLAG;
			pci_read_config_dword(dev,
				pos + PCI_ERR_HEADER_LOG, &info->tlp.dw0);
			pci_read_config_dword(dev,
				pos + PCI_ERR_HEADER_LOG + 4, &info->tlp.dw1);
			pci_read_config_dword(dev,
				pos + PCI_ERR_HEADER_LOG + 8, &info->tlp.dw2);
			pci_read_config_dword(dev,
				pos + PCI_ERR_HEADER_LOG + 12, &info->tlp.dw3);
		}
	}

	return AER_SUCCESS;
}
Beispiel #9
0
/**
 * aer_root_reset - reset link on Root Port
 * @dev: pointer to Root Port's pci_dev data structure
 *
 * Invoked by Port Bus driver when performing link reset at Root Port.
 **/
static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
{
    u16 p2p_ctrl;
    u32 status;
    int pos;

    pos = pci_find_aer_capability(dev);

    /* Disable Root's interrupt in response to error messages */
    pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, 0);

    /* Assert Secondary Bus Reset */
    pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &p2p_ctrl);
    p2p_ctrl |= PCI_CB_BRIDGE_CTL_CB_RESET;
    pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);

    /* De-assert Secondary Bus Reset */
    p2p_ctrl &= ~PCI_CB_BRIDGE_CTL_CB_RESET;
    pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);

    /*
     * System software must wait for at least 100ms from the end
     * of a reset of one or more device before it is permitted
     * to issue Configuration Requests to those devices.
     */
    msleep(200);
    printk(KERN_DEBUG "Complete link reset at Root[%s]\n", dev->dev.bus_id);

    /* Enable Root Port's interrupt in response to error messages */
    pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &status);
    pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, status);
    pci_write_config_dword(dev,
        pos + PCI_ERR_ROOT_COMMAND,
        ROOT_PORT_INTR_ON_MESG_MASK);

    return PCI_ERS_RESULT_RECOVERED;
}