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, ®16); 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