aerdrv.c revision d43c36dc6b357fa1806800f18aa30123c747a6d1
16c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin/*
26c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * drivers/pci/pcie/aer/aerdrv.c
36c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin *
46c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * This file is subject to the terms and conditions of the GNU General Public
56c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * License.  See the file "COPYING" in the main directory of this archive
66c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * for more details.
76c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin *
86c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * This file implements the AER root port service driver. The driver will
96c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * register an irq handler. When root port triggers an AER interrupt, the irq
106c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * handler will collect root port status and schedule a work.
116c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin *
126c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * Copyright (C) 2006 Intel Corp.
136c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin *	Tom Long Nguyen (tom.l.nguyen@intel.com)
146c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin *	Zhang Yanmin (yanmin.zhang@intel.com)
156c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin *
166c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin */
176c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
186c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin#include <linux/module.h>
196c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin#include <linux/pci.h>
20d43c36dc6b357fa1806800f18aa30123c747a6d1Alexey Dobriyan#include <linux/sched.h>
216c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin#include <linux/kernel.h>
226c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin#include <linux/errno.h>
236c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin#include <linux/pm.h>
246c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin#include <linux/init.h>
256c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin#include <linux/interrupt.h>
266c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin#include <linux/delay.h>
276c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin#include <linux/pcieport_if.h>
286c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
296c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin#include "aerdrv.h"
305d9526d07a8dc87460c13c277b3edcc26b0e662fAlex Chiang#include "../../pci.h"
316c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
326c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin/*
336c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * Version Information
346c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin */
356c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin#define DRIVER_VERSION "v1.0"
366c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin#define DRIVER_AUTHOR "tom.l.nguyen@intel.com"
376c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin#define DRIVER_DESC "Root Port Advanced Error Reporting Driver"
386c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, YanminMODULE_AUTHOR(DRIVER_AUTHOR);
396c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, YanminMODULE_DESCRIPTION(DRIVER_DESC);
406c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, YanminMODULE_LICENSE("GPL");
416c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
42c9a918838c07cbef934c8ef818d8f0e719015c3aHidetoshi Setostatic int __devinit aer_probe(struct pcie_device *dev);
436c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanminstatic void aer_remove(struct pcie_device *dev);
446c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanminstatic pci_ers_result_t aer_error_detected(struct pci_dev *dev,
456c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	enum pci_channel_state error);
466c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanminstatic void aer_error_resume(struct pci_dev *dev);
476c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanminstatic pci_ers_result_t aer_root_reset(struct pci_dev *dev);
486c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
496c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanminstatic struct pci_error_handlers aer_error_handlers = {
506c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	.error_detected = aer_error_detected,
51c9a918838c07cbef934c8ef818d8f0e719015c3aHidetoshi Seto	.resume		= aer_error_resume,
526c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin};
536c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
54c1996c2778e90f80cb9fc6a52508068f10d39611Sam Ravnborgstatic struct pcie_port_service_driver aerdriver = {
556c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	.name		= "aer",
5622106368c999246c414610dcaacd485e741605b1Rafael J. Wysocki	.port_type	= PCIE_ANY_PORT,
5722106368c999246c414610dcaacd485e741605b1Rafael J. Wysocki	.service	= PCIE_PORT_SERVICE_AER,
586c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
596c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	.probe		= aer_probe,
606c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	.remove		= aer_remove,
616c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
626c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	.err_handler	= &aer_error_handlers,
636c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
646c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	.reset_link	= aer_root_reset,
656c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin};
666c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
677f785763660e75c9eddaddea3d618696af4ae3a2Randy Dunlapstatic int pcie_aer_disable;
687f785763660e75c9eddaddea3d618696af4ae3a2Randy Dunlap
697f785763660e75c9eddaddea3d618696af4ae3a2Randy Dunlapvoid pci_no_aer(void)
707f785763660e75c9eddaddea3d618696af4ae3a2Randy Dunlap{
717f785763660e75c9eddaddea3d618696af4ae3a2Randy Dunlap	pcie_aer_disable = 1;	/* has priority over 'forceload' */
727f785763660e75c9eddaddea3d618696af4ae3a2Randy Dunlap}
737f785763660e75c9eddaddea3d618696af4ae3a2Randy Dunlap
746c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin/**
756c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * aer_irq - Root Port's ISR
766c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * @irq: IRQ assigned to Root Port
776c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * @context: pointer to Root Port data structure
786c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin *
796c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * Invoked when Root Port detects AER messages.
806c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin **/
81634deb028c9188b4144863ea87dde5457fb93e92Huang Yingirqreturn_t aer_irq(int irq, void *context)
826c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin{
836c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	unsigned int status, id;
846c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	struct pcie_device *pdev = (struct pcie_device *)context;
856c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	struct aer_rpc *rpc = get_service_data(pdev);
866c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	int next_prod_idx;
876c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	unsigned long flags;
886c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	int pos;
896c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
900927678f55c9a50c296f7e6dae85e87b8236e155Jesse Barnes	pos = pci_find_ext_capability(pdev->port, PCI_EXT_CAP_ID_ERR);
916c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	/*
926c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	 * Must lock access to Root Error Status Reg, Root Error ID Reg,
936c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	 * and Root error producer/consumer index
946c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	 */
956c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	spin_lock_irqsave(&rpc->e_lock, flags);
966c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
976c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	/* Read error status */
986c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, &status);
996c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	if (!(status & ROOT_ERR_STATUS_MASKS)) {
1006c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin		spin_unlock_irqrestore(&rpc->e_lock, flags);
1016c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin		return IRQ_NONE;
1026c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	}
1036c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
1046c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	/* Read error source and clear error status */
1056c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_COR_SRC, &id);
1066c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	pci_write_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, status);
1076c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
1086c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	/* Store error source for later DPC handler */
1096c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	next_prod_idx = rpc->prod_idx + 1;
1106c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	if (next_prod_idx == AER_ERROR_SOURCES_MAX)
1116c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin		next_prod_idx = 0;
1126c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	if (next_prod_idx == rpc->cons_idx) {
1136c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin		/*
1146c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin		 * Error Storm Condition - possibly the same error occurred.
1156c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin		 * Drop the error.
1166c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin		 */
1176c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin		spin_unlock_irqrestore(&rpc->e_lock, flags);
1186c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin		return IRQ_HANDLED;
1196c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	}
1206c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	rpc->e_sources[rpc->prod_idx].status =  status;
1216c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	rpc->e_sources[rpc->prod_idx].id = id;
1226c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	rpc->prod_idx = next_prod_idx;
1236c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	spin_unlock_irqrestore(&rpc->e_lock, flags);
1246c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
1256c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	/*  Invoke DPC handler */
1266c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	schedule_work(&rpc->dpc_handler);
1276c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
1286c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	return IRQ_HANDLED;
1296c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin}
130634deb028c9188b4144863ea87dde5457fb93e92Huang YingEXPORT_SYMBOL_GPL(aer_irq);
1316c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
1326c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin/**
1336c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * aer_alloc_rpc - allocate Root Port data structure
1346c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * @dev: pointer to the pcie_dev data structure
1356c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin *
1366c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * Invoked when Root Port's AER service is loaded.
1376c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin **/
138c9a918838c07cbef934c8ef818d8f0e719015c3aHidetoshi Setostatic struct aer_rpc *aer_alloc_rpc(struct pcie_device *dev)
1396c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin{
1406c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	struct aer_rpc *rpc;
1416c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
142c9a918838c07cbef934c8ef818d8f0e719015c3aHidetoshi Seto	rpc = kzalloc(sizeof(struct aer_rpc), GFP_KERNEL);
143c9a918838c07cbef934c8ef818d8f0e719015c3aHidetoshi Seto	if (!rpc)
1446c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin		return NULL;
1456c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
1466c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	/*
1476c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	 * Initialize Root lock access, e_lock, to Root Error Status Reg,
1486c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	 * Root Error ID Reg, and Root error producer/consumer index.
1496c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	 */
150f5609d7e679db3f29433f56e1f2e397a2f815288Milind Arun Choudhary	spin_lock_init(&rpc->e_lock);
1516c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
1526c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	rpc->rpd = dev;
15365f27f38446e1976cc98fd3004b110fedcddd189David Howells	INIT_WORK(&rpc->dpc_handler, aer_isr);
1546c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	rpc->prod_idx = rpc->cons_idx = 0;
1556c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	mutex_init(&rpc->rpc_mutex);
1566c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	init_waitqueue_head(&rpc->wait_release);
1576c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
1586c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	/* Use PCIE bus function to store rpc into PCIE device */
1596c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	set_service_data(dev, rpc);
1606c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
1616c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	return rpc;
1626c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin}
1636c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
1646c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin/**
1656c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * aer_remove - clean up resources
1666c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * @dev: pointer to the pcie_dev data structure
1676c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin *
1686c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * Invoked when PCI Express bus unloads or AER probe fails.
1696c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin **/
1706c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanminstatic void aer_remove(struct pcie_device *dev)
1716c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin{
1726c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	struct aer_rpc *rpc = get_service_data(dev);
1736c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
1746c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	if (rpc) {
1756c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin		/* If register interrupt service, it must be free. */
1766c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin		if (rpc->isr)
1776c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin			free_irq(dev->irq, dev);
1786c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
1796c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin		wait_event(rpc->wait_release, rpc->prod_idx == rpc->cons_idx);
1806c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
1816c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin		aer_delete_rootport(rpc);
1826c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin		set_service_data(dev, NULL);
1836c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	}
1846c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin}
1856c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
1866c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin/**
1876c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * aer_probe - initialize resources
1886c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * @dev: pointer to the pcie_dev data structure
1896c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * @id: pointer to the service id data structure
1906c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin *
1916c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * Invoked when PCI Express bus loads AER service driver.
1926c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin **/
193c9a918838c07cbef934c8ef818d8f0e719015c3aHidetoshi Setostatic int __devinit aer_probe(struct pcie_device *dev)
1946c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin{
1956c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	int status;
1966c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	struct aer_rpc *rpc;
1976c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	struct device *device = &dev->device;
1986c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
1996c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	/* Init */
200c9a918838c07cbef934c8ef818d8f0e719015c3aHidetoshi Seto	status = aer_init(dev);
201c9a918838c07cbef934c8ef818d8f0e719015c3aHidetoshi Seto	if (status)
2026c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin		return status;
2036c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
2046c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	/* Alloc rpc data structure */
205c9a918838c07cbef934c8ef818d8f0e719015c3aHidetoshi Seto	rpc = aer_alloc_rpc(dev);
206c9a918838c07cbef934c8ef818d8f0e719015c3aHidetoshi Seto	if (!rpc) {
207531f254e5cdadb894f04ed27107cdb34c15817eaBjorn Helgaas		dev_printk(KERN_DEBUG, device, "alloc rpc failed\n");
2086c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin		aer_remove(dev);
2096c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin		return -ENOMEM;
2106c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	}
2116c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
2126c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	/* Request IRQ ISR */
213c9a918838c07cbef934c8ef818d8f0e719015c3aHidetoshi Seto	status = request_irq(dev->irq, aer_irq, IRQF_SHARED, "aerdrv", dev);
214c9a918838c07cbef934c8ef818d8f0e719015c3aHidetoshi Seto	if (status) {
215531f254e5cdadb894f04ed27107cdb34c15817eaBjorn Helgaas		dev_printk(KERN_DEBUG, device, "request IRQ failed\n");
2166c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin		aer_remove(dev);
2176c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin		return status;
2186c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	}
2196c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
2206c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	rpc->isr = 1;
2216c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
2226c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	aer_enable_rootport(rpc);
2236c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
2246c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	return status;
2256c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin}
2266c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
2276c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin/**
2286c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * aer_root_reset - reset link on Root Port
2296c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * @dev: pointer to Root Port's pci_dev data structure
2306c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin *
2316c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * Invoked by Port Bus driver when performing link reset at Root Port.
2326c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin **/
2336c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanminstatic pci_ers_result_t aer_root_reset(struct pci_dev *dev)
2346c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin{
2356c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	u16 p2p_ctrl;
2366c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	u32 status;
2376c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	int pos;
2386c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
2390927678f55c9a50c296f7e6dae85e87b8236e155Jesse Barnes	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
2406c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
2416c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	/* Disable Root's interrupt in response to error messages */
2426c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, 0);
2436c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
2446c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	/* Assert Secondary Bus Reset */
2456c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &p2p_ctrl);
2466c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	p2p_ctrl |= PCI_CB_BRIDGE_CTL_CB_RESET;
2476c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
2486c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
2496c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	/* De-assert Secondary Bus Reset */
2506c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	p2p_ctrl &= ~PCI_CB_BRIDGE_CTL_CB_RESET;
2516c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
2526c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
2536c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	/*
2546c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	 * System software must wait for at least 100ms from the end
2556c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	 * of a reset of one or more device before it is permitted
2566c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	 * to issue Configuration Requests to those devices.
2576c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	 */
2586c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	msleep(200);
259531f254e5cdadb894f04ed27107cdb34c15817eaBjorn Helgaas	dev_printk(KERN_DEBUG, &dev->dev, "Root Port link has been reset\n");
2606c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
2616c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	/* Enable Root Port's interrupt in response to error messages */
2626c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &status);
2636c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, status);
2646c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	pci_write_config_dword(dev,
2656c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin		pos + PCI_ERR_ROOT_COMMAND,
2666c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin		ROOT_PORT_INTR_ON_MESG_MASK);
2676c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
2686c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	return PCI_ERS_RESULT_RECOVERED;
2696c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin}
2706c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
2716c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin/**
2726c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * aer_error_detected - update severity status
2736c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * @dev: pointer to Root Port's pci_dev data structure
2746c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * @error: error severity being notified by port bus
2756c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin *
2766c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * Invoked by Port Bus driver during error recovery.
2776c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin **/
2786c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanminstatic pci_ers_result_t aer_error_detected(struct pci_dev *dev,
2796c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin			enum pci_channel_state error)
2806c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin{
2816c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	/* Root Port has no impact. Always recovers. */
2826c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	return PCI_ERS_RESULT_CAN_RECOVER;
2836c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin}
2846c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
2856c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin/**
2866c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * aer_error_resume - clean up corresponding error status bits
2876c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * @dev: pointer to Root Port's pci_dev data structure
2886c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin *
2896c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * Invoked by Port Bus driver during nonfatal recovery.
2906c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin **/
2916c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanminstatic void aer_error_resume(struct pci_dev *dev)
2926c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin{
2936c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	int pos;
2946c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	u32 status, mask;
2956c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	u16 reg16;
2966c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
2976c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	/* Clean up Root device status */
2986c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
2996c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, &reg16);
3006c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	pci_write_config_word(dev, pos + PCI_EXP_DEVSTA, reg16);
3016c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
3026c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	/* Clean AER Root Error Status */
3030927678f55c9a50c296f7e6dae85e87b8236e155Jesse Barnes	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
3046c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
3056c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask);
3066c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	if (dev->error_state == pci_channel_io_normal)
3076c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin		status &= ~mask; /* Clear corresponding nonfatal bits */
3086c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	else
3096c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin		status &= mask; /* Clear corresponding fatal bits */
3106c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin	pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status);
3116c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin}
3126c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
3136c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin/**
3146c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * aer_service_init - register AER root service driver
3156c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin *
3166c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * Invoked when AER root service driver is loaded.
3176c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin **/
3186c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanminstatic int __init aer_service_init(void)
3196c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin{
3207f785763660e75c9eddaddea3d618696af4ae3a2Randy Dunlap	if (pcie_aer_disable)
3217f785763660e75c9eddaddea3d618696af4ae3a2Randy Dunlap		return -ENXIO;
3223e77a3f7895e9c20756dc250282afa12f6d259a3Andi Kleen	if (!pci_msi_enabled())
3233e77a3f7895e9c20756dc250282afa12f6d259a3Andi Kleen		return -ENXIO;
324c1996c2778e90f80cb9fc6a52508068f10d39611Sam Ravnborg	return pcie_port_service_register(&aerdriver);
3256c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin}
3266c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
3276c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin/**
3286c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * aer_service_exit - unregister AER root service driver
3296c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin *
3306c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin * Invoked when AER root service driver is unloaded.
3316c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin **/
3326c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanminstatic void __exit aer_service_exit(void)
3336c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin{
334c1996c2778e90f80cb9fc6a52508068f10d39611Sam Ravnborg	pcie_port_service_unregister(&aerdriver);
3356c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin}
3366c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanmin
3376c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanminmodule_init(aer_service_init);
3386c2b374d74857e892080ee726184ec1d15e7d4e4Zhang, Yanminmodule_exit(aer_service_exit);
339