11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * File: portdrv_pci.c 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Purpose: PCI Express Port Bus Driver 41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 2004 Intel 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h> 101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/pci.h> 111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h> 121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h> 131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/pm.h> 141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h> 151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/pcieport_if.h> 164bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin#include <linux/aer.h> 17c39fae1416d59fd565606793f090cebe3720d50dRafael J. Wysocki#include <linux/dmi.h> 1828eb5f274a305bf3a13b2c80c4804d4515d05c64Rafael J. Wysocki#include <linux/pci-aspm.h> 191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "portdrv.h" 214bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin#include "aer/aerdrv.h" 221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Version Information 251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_VERSION "v1.0" 271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_AUTHOR "tom.l.nguyen@intel.com" 287e8af37a9a71b479f58d2fd5f0ddaa6780c51f11Stefan Assmann#define DRIVER_DESC "PCIe Port Bus Driver" 291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR(DRIVER_AUTHOR); 301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION(DRIVER_DESC); 311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL"); 321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3379dd9182db2072d63ccf160bb9a3463b1c952723Rafael J. Wysocki/* If this switch is set, PCIe port native services should not be enabled. */ 3479dd9182db2072d63ccf160bb9a3463b1c952723Rafael J. Wysockibool pcie_ports_disabled; 3579dd9182db2072d63ccf160bb9a3463b1c952723Rafael J. Wysocki 3628eb5f274a305bf3a13b2c80c4804d4515d05c64Rafael J. Wysocki/* 3728eb5f274a305bf3a13b2c80c4804d4515d05c64Rafael J. Wysocki * If this switch is set, ACPI _OSC will be used to determine whether or not to 3828eb5f274a305bf3a13b2c80c4804d4515d05c64Rafael J. Wysocki * enable PCIe port native services. 3928eb5f274a305bf3a13b2c80c4804d4515d05c64Rafael J. Wysocki */ 4028eb5f274a305bf3a13b2c80c4804d4515d05c64Rafael J. Wysockibool pcie_ports_auto = true; 4128eb5f274a305bf3a13b2c80c4804d4515d05c64Rafael J. Wysocki 4279dd9182db2072d63ccf160bb9a3463b1c952723Rafael J. Wysockistatic int __init pcie_port_setup(char *str) 4379dd9182db2072d63ccf160bb9a3463b1c952723Rafael J. Wysocki{ 4428eb5f274a305bf3a13b2c80c4804d4515d05c64Rafael J. Wysocki if (!strncmp(str, "compat", 6)) { 4579dd9182db2072d63ccf160bb9a3463b1c952723Rafael J. Wysocki pcie_ports_disabled = true; 4628eb5f274a305bf3a13b2c80c4804d4515d05c64Rafael J. Wysocki } else if (!strncmp(str, "native", 6)) { 4728eb5f274a305bf3a13b2c80c4804d4515d05c64Rafael J. Wysocki pcie_ports_disabled = false; 4828eb5f274a305bf3a13b2c80c4804d4515d05c64Rafael J. Wysocki pcie_ports_auto = false; 4928eb5f274a305bf3a13b2c80c4804d4515d05c64Rafael J. Wysocki } else if (!strncmp(str, "auto", 4)) { 5028eb5f274a305bf3a13b2c80c4804d4515d05c64Rafael J. Wysocki pcie_ports_disabled = false; 5128eb5f274a305bf3a13b2c80c4804d4515d05c64Rafael J. Wysocki pcie_ports_auto = true; 5228eb5f274a305bf3a13b2c80c4804d4515d05c64Rafael J. Wysocki } 5379dd9182db2072d63ccf160bb9a3463b1c952723Rafael J. Wysocki 5479dd9182db2072d63ccf160bb9a3463b1c952723Rafael J. Wysocki return 1; 5579dd9182db2072d63ccf160bb9a3463b1c952723Rafael J. Wysocki} 5679dd9182db2072d63ccf160bb9a3463b1c952723Rafael J. Wysocki__setup("pcie_ports=", pcie_port_setup); 5779dd9182db2072d63ccf160bb9a3463b1c952723Rafael J. Wysocki 581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* global data */ 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 60fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki/** 61fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki * pcie_clear_root_pme_status - Clear root port PME interrupt status. 62fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki * @dev: PCIe root port or event collector. 63fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki */ 64fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysockivoid pcie_clear_root_pme_status(struct pci_dev *dev) 65fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki{ 66fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki int rtsta_pos; 67fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki u32 rtsta; 68fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki 69fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki rtsta_pos = pci_pcie_cap(dev) + PCI_EXP_RTSTA; 70fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki 71fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki pci_read_config_dword(dev, rtsta_pos, &rtsta); 72fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki rtsta |= PCI_EXP_RTSTA_PME; 73fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki pci_write_config_dword(dev, rtsta_pos, rtsta); 74fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki} 75fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki 764bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanminstatic int pcie_portdrv_restore_config(struct pci_dev *dev) 774bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin{ 784bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin int retval; 794bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 804bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin retval = pci_enable_device(dev); 814bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin if (retval) 824bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin return retval; 834bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin pci_set_master(dev); 844bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin return 0; 854bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin} 864bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 870bed208efcb25bed4dc2026488a4417aa68e7c92Zhang, Yanmin#ifdef CONFIG_PM 88fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysockistatic int pcie_port_resume_noirq(struct device *dev) 89fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki{ 90fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki struct pci_dev *pdev = to_pci_dev(dev); 91fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki 92fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki /* 93fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki * Some BIOSes forget to clear Root PME Status bits after system wakeup 94fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki * which breaks ACPI-based runtime wakeup on PCI Express, so clear those 95fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki * bits now just in case (shouldn't hurt). 96fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki */ 97fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki if(pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT) 98fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki pcie_clear_root_pme_status(pdev); 99fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki return 0; 100fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki} 101fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki 102471452104b8520337ae2fb48c4e61cd4896e025dAlexey Dobriyanstatic const struct dev_pm_ops pcie_portdrv_pm_ops = { 1033a3c244c9a355105bc193fde873c73727bf87192Rafael J. Wysocki .suspend = pcie_port_device_suspend, 1043a3c244c9a355105bc193fde873c73727bf87192Rafael J. Wysocki .resume = pcie_port_device_resume, 1053a3c244c9a355105bc193fde873c73727bf87192Rafael J. Wysocki .freeze = pcie_port_device_suspend, 1063a3c244c9a355105bc193fde873c73727bf87192Rafael J. Wysocki .thaw = pcie_port_device_resume, 1073a3c244c9a355105bc193fde873c73727bf87192Rafael J. Wysocki .poweroff = pcie_port_device_suspend, 1083a3c244c9a355105bc193fde873c73727bf87192Rafael J. Wysocki .restore = pcie_port_device_resume, 109fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki .resume_noirq = pcie_port_resume_noirq, 1103a3c244c9a355105bc193fde873c73727bf87192Rafael J. Wysocki}; 1114bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 1123a3c244c9a355105bc193fde873c73727bf87192Rafael J. Wysocki#define PCIE_PORTDRV_PM_OPS (&pcie_portdrv_pm_ops) 113a79d682f789730dfabaebbb507c87a90c0671a62Rafael J. Wysocki 1143a3c244c9a355105bc193fde873c73727bf87192Rafael J. Wysocki#else /* !PM */ 1153a3c244c9a355105bc193fde873c73727bf87192Rafael J. Wysocki 1163a3c244c9a355105bc193fde873c73727bf87192Rafael J. Wysocki#define PCIE_PORTDRV_PM_OPS NULL 1173a3c244c9a355105bc193fde873c73727bf87192Rafael J. Wysocki#endif /* !PM */ 1184bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * pcie_portdrv_probe - Probe PCI-Express port devices 1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @dev: PCI-Express port device being probed 1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 12340da4186a53e59d801130156ecb89fc5830ff227Hidetoshi Seto * If detected invokes the pcie_port_device_register() method for 1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * this port device. 1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 127898294c97500b1cdff6edce52fd34e024eb070ecKenji Kaneshigestatic int __devinit pcie_portdrv_probe(struct pci_dev *dev, 128898294c97500b1cdff6edce52fd34e024eb070ecKenji Kaneshige const struct pci_device_id *id) 1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 130898294c97500b1cdff6edce52fd34e024eb070ecKenji Kaneshige int status; 1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 132898294c97500b1cdff6edce52fd34e024eb070ecKenji Kaneshige if (!pci_is_pcie(dev) || 133898294c97500b1cdff6edce52fd34e024eb070ecKenji Kaneshige ((dev->pcie_type != PCI_EXP_TYPE_ROOT_PORT) && 134898294c97500b1cdff6edce52fd34e024eb070ecKenji Kaneshige (dev->pcie_type != PCI_EXP_TYPE_UPSTREAM) && 135898294c97500b1cdff6edce52fd34e024eb070ecKenji Kaneshige (dev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM))) 136898294c97500b1cdff6edce52fd34e024eb070ecKenji Kaneshige return -ENODEV; 1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 13840da4186a53e59d801130156ecb89fc5830ff227Hidetoshi Seto if (!dev->irq && dev->pin) { 13934a2e15e95fce6d6f4d30162f53a0ceb25d5bbafBjorn Helgaas dev_warn(&dev->dev, "device [%04x:%04x] has invalid IRQ; " 14034438ba602f9b8904aafe7559046ea68e99e88a0Bjorn Helgaas "check vendor BIOS\n", dev->vendor, dev->device); 1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 142f118c0c3cff4fed39bde1863f9d59850719645ccRafael J. Wysocki status = pcie_port_device_register(dev); 143f118c0c3cff4fed39bde1863f9d59850719645ccRafael J. Wysocki if (status) 144f118c0c3cff4fed39bde1863f9d59850719645ccRafael J. Wysocki return status; 1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 14687d2e2ecf6026efa64b01f7f71802b20da736d35Rafael J. Wysocki pci_save_state(dev); 1474bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 15140da4186a53e59d801130156ecb89fc5830ff227Hidetoshi Setostatic void pcie_portdrv_remove(struct pci_dev *dev) 1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pcie_port_device_remove(dev); 154d89987193631bf23d1735c55d13a06d4b8d0e9bdAlex Chiang pci_disable_device(dev); 1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1574bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanminstatic int error_detected_iter(struct device *device, void *data) 15860854838596906ebae7f32e4de921e150e669b60Henrik Kretzschmar{ 1594bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin struct pcie_device *pcie_device; 1604bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin struct pcie_port_service_driver *driver; 1614bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin struct aer_broadcast_data *result_data; 1624bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin pci_ers_result_t status; 1634bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 1644bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin result_data = (struct aer_broadcast_data *) data; 1654bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 1664bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin if (device->bus == &pcie_port_bus_type && device->driver) { 1674bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin driver = to_service_driver(device->driver); 1684bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin if (!driver || 1694bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin !driver->err_handler || 1704bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin !driver->err_handler->error_detected) 1714bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin return 0; 1724bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 1734bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin pcie_device = to_pcie_device(device); 1744bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 1754bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin /* Forward error detected message to service drivers */ 1764bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin status = driver->err_handler->error_detected( 1774bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin pcie_device->port, 1784bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin result_data->state); 1794bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin result_data->result = 1804bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin merge_result(result_data->result, status); 1814bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin } 1824bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 1834bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin return 0; 18460854838596906ebae7f32e4de921e150e669b60Henrik Kretzschmar} 18560854838596906ebae7f32e4de921e150e669b60Henrik Kretzschmar 1864bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanminstatic pci_ers_result_t pcie_portdrv_error_detected(struct pci_dev *dev, 1874bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin enum pci_channel_state error) 18860854838596906ebae7f32e4de921e150e669b60Henrik Kretzschmar{ 18940da4186a53e59d801130156ecb89fc5830ff227Hidetoshi Seto struct aer_broadcast_data data = {error, PCI_ERS_RESULT_CAN_RECOVER}; 19040da4186a53e59d801130156ecb89fc5830ff227Hidetoshi Seto int ret; 1914bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 192b19441af185559118e8247382ea4f2f76ebffc6dGreg Kroah-Hartman /* can not fail */ 19340da4186a53e59d801130156ecb89fc5830ff227Hidetoshi Seto ret = device_for_each_child(&dev->dev, &data, error_detected_iter); 1944bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 19540da4186a53e59d801130156ecb89fc5830ff227Hidetoshi Seto return data.result; 1964bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin} 1974bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 1984bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanminstatic int mmio_enabled_iter(struct device *device, void *data) 1994bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin{ 2004bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin struct pcie_device *pcie_device; 2014bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin struct pcie_port_service_driver *driver; 2024bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin pci_ers_result_t status, *result; 2034bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 2044bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin result = (pci_ers_result_t *) data; 2054bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 2064bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin if (device->bus == &pcie_port_bus_type && device->driver) { 2074bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin driver = to_service_driver(device->driver); 2084bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin if (driver && 2094bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin driver->err_handler && 2104bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin driver->err_handler->mmio_enabled) { 2114bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin pcie_device = to_pcie_device(device); 2124bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 2134bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin /* Forward error message to service drivers */ 2144bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin status = driver->err_handler->mmio_enabled( 2154bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin pcie_device->port); 2164bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin *result = merge_result(*result, status); 2174bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin } 2184bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin } 21960854838596906ebae7f32e4de921e150e669b60Henrik Kretzschmar 22060854838596906ebae7f32e4de921e150e669b60Henrik Kretzschmar return 0; 22160854838596906ebae7f32e4de921e150e669b60Henrik Kretzschmar} 22260854838596906ebae7f32e4de921e150e669b60Henrik Kretzschmar 2234bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanminstatic pci_ers_result_t pcie_portdrv_mmio_enabled(struct pci_dev *dev) 2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2254bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED; 226b19441af185559118e8247382ea4f2f76ebffc6dGreg Kroah-Hartman int retval; 2275823d100ae260d022b4dd5ec9cc0b85f0bf0d646long 228b19441af185559118e8247382ea4f2f76ebffc6dGreg Kroah-Hartman /* get true return value from &status */ 229b19441af185559118e8247382ea4f2f76ebffc6dGreg Kroah-Hartman retval = device_for_each_child(&dev->dev, &status, mmio_enabled_iter); 2304bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin return status; 2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2334bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanminstatic int slot_reset_iter(struct device *device, void *data) 2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2354bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin struct pcie_device *pcie_device; 2364bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin struct pcie_port_service_driver *driver; 2374bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin pci_ers_result_t status, *result; 2384bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 2394bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin result = (pci_ers_result_t *) data; 2404bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 2414bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin if (device->bus == &pcie_port_bus_type && device->driver) { 2424bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin driver = to_service_driver(device->driver); 2434bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin if (driver && 2444bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin driver->err_handler && 2454bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin driver->err_handler->slot_reset) { 2464bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin pcie_device = to_pcie_device(device); 2474bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 2484bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin /* Forward error message to service drivers */ 2494bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin status = driver->err_handler->slot_reset( 2504bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin pcie_device->port); 2514bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin *result = merge_result(*result, status); 2524bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin } 2534bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin } 2544bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 2554bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin return 0; 2564bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin} 2574bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 2584bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanminstatic pci_ers_result_t pcie_portdrv_slot_reset(struct pci_dev *dev) 2594bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin{ 260029091df01116aff8dea89ce96a0a2534401803aZhang, Yanmin pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED; 261b19441af185559118e8247382ea4f2f76ebffc6dGreg Kroah-Hartman int retval; 2624bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 2634bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin /* If fatal, restore cfg space for possible link reset at upstream */ 2644bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin if (dev->error_state == pci_channel_io_frozen) { 265e9d8288871efa0d98a1d1d1f17976b5b00a0234dRafael J. Wysocki dev->state_saved = true; 266a79d682f789730dfabaebbb507c87a90c0671a62Rafael J. Wysocki pci_restore_state(dev); 2674bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin pcie_portdrv_restore_config(dev); 2684bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin pci_enable_pcie_error_reporting(dev); 2694bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin } 2704bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 271b19441af185559118e8247382ea4f2f76ebffc6dGreg Kroah-Hartman /* get true return value from &status */ 272b19441af185559118e8247382ea4f2f76ebffc6dGreg Kroah-Hartman retval = device_for_each_child(&dev->dev, &status, slot_reset_iter); 2734bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 2744bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin return status; 2754bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin} 2764bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 2774bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanminstatic int resume_iter(struct device *device, void *data) 2784bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin{ 2794bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin struct pcie_device *pcie_device; 2804bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin struct pcie_port_service_driver *driver; 2814bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 2824bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin if (device->bus == &pcie_port_bus_type && device->driver) { 2834bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin driver = to_service_driver(device->driver); 2844bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin if (driver && 2854bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin driver->err_handler && 2864bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin driver->err_handler->resume) { 2874bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin pcie_device = to_pcie_device(device); 2884bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 2894bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin /* Forward error message to service drivers */ 2904bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin driver->err_handler->resume(pcie_device->port); 2914bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin } 2924bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin } 2934bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 2944bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin return 0; 2954bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin} 2964bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 2974bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanminstatic void pcie_portdrv_err_resume(struct pci_dev *dev) 2984bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin{ 299b19441af185559118e8247382ea4f2f76ebffc6dGreg Kroah-Hartman int retval; 300b19441af185559118e8247382ea4f2f76ebffc6dGreg Kroah-Hartman /* nothing to do with error value, if it ever happens */ 301b19441af185559118e8247382ea4f2f76ebffc6dGreg Kroah-Hartman retval = device_for_each_child(&dev->dev, NULL, resume_iter); 3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * LINUX Device Driver Model 3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const struct pci_device_id port_pci_ids[] = { { 3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* handle any PCI-Express port */ 3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ~0), 3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds }, { /* end: all zeroes */ } 3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DEVICE_TABLE(pci, port_pci_ids); 3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3144bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanminstatic struct pci_error_handlers pcie_portdrv_err_handler = { 3154bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin .error_detected = pcie_portdrv_error_detected, 3164bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin .mmio_enabled = pcie_portdrv_mmio_enabled, 3174bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin .slot_reset = pcie_portdrv_slot_reset, 3184bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin .resume = pcie_portdrv_err_resume, 3194bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin}; 3204bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin 3213603a6a37c5f5c6a2b109fdb48c9456f08aebdd8Sam Ravnborgstatic struct pci_driver pcie_portdriver = { 322e3fb20f9c8783d6e27cf84389a9606e410733eefBjorn Helgaas .name = "pcieport", 3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .id_table = &port_pci_ids[0], 3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .probe = pcie_portdrv_probe, 3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .remove = pcie_portdrv_remove, 3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3284bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837Zhang, Yanmin .err_handler = &pcie_portdrv_err_handler, 3293a3c244c9a355105bc193fde873c73727bf87192Rafael J. Wysocki 3303a3c244c9a355105bc193fde873c73727bf87192Rafael J. Wysocki .driver.pm = PCIE_PORTDRV_PM_OPS, 3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 333c39fae1416d59fd565606793f090cebe3720d50dRafael J. Wysockistatic int __init dmi_pcie_pme_disable_msi(const struct dmi_system_id *d) 334c39fae1416d59fd565606793f090cebe3720d50dRafael J. Wysocki{ 335c39fae1416d59fd565606793f090cebe3720d50dRafael J. Wysocki pr_notice("%s detected: will not use MSI for PCIe PME signaling\n", 336c39fae1416d59fd565606793f090cebe3720d50dRafael J. Wysocki d->ident); 337c39fae1416d59fd565606793f090cebe3720d50dRafael J. Wysocki pcie_pme_disable_msi(); 338c39fae1416d59fd565606793f090cebe3720d50dRafael J. Wysocki return 0; 339c39fae1416d59fd565606793f090cebe3720d50dRafael J. Wysocki} 340c39fae1416d59fd565606793f090cebe3720d50dRafael J. Wysocki 341c39fae1416d59fd565606793f090cebe3720d50dRafael J. Wysockistatic struct dmi_system_id __initdata pcie_portdrv_dmi_table[] = { 342c39fae1416d59fd565606793f090cebe3720d50dRafael J. Wysocki /* 343c39fae1416d59fd565606793f090cebe3720d50dRafael J. Wysocki * Boxes that should not use MSI for PCIe PME signaling. 344c39fae1416d59fd565606793f090cebe3720d50dRafael J. Wysocki */ 345c39fae1416d59fd565606793f090cebe3720d50dRafael J. Wysocki { 346c39fae1416d59fd565606793f090cebe3720d50dRafael J. Wysocki .callback = dmi_pcie_pme_disable_msi, 347c39fae1416d59fd565606793f090cebe3720d50dRafael J. Wysocki .ident = "MSI Wind U-100", 348c39fae1416d59fd565606793f090cebe3720d50dRafael J. Wysocki .matches = { 349c39fae1416d59fd565606793f090cebe3720d50dRafael J. Wysocki DMI_MATCH(DMI_SYS_VENDOR, 350c39fae1416d59fd565606793f090cebe3720d50dRafael J. Wysocki "MICRO-STAR INTERNATIONAL CO., LTD"), 351c39fae1416d59fd565606793f090cebe3720d50dRafael J. Wysocki DMI_MATCH(DMI_PRODUCT_NAME, "U-100"), 352c39fae1416d59fd565606793f090cebe3720d50dRafael J. Wysocki }, 353c39fae1416d59fd565606793f090cebe3720d50dRafael J. Wysocki }, 354c39fae1416d59fd565606793f090cebe3720d50dRafael J. Wysocki {} 355c39fae1416d59fd565606793f090cebe3720d50dRafael J. Wysocki}; 356c39fae1416d59fd565606793f090cebe3720d50dRafael J. Wysocki 3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init pcie_portdrv_init(void) 3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 35920d516602c022997feb24a9f1a806fc986b9e4e8Randy Dunlap int retval; 3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 361fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki if (pcie_ports_disabled) 362fe31e69740eddc7316071ed5165fed6703c8cd12Rafael J. Wysocki return pci_register_driver(&pcie_portdriver); 36379dd9182db2072d63ccf160bb9a3463b1c952723Rafael J. Wysocki 364c39fae1416d59fd565606793f090cebe3720d50dRafael J. Wysocki dmi_check_system(pcie_portdrv_dmi_table); 365c39fae1416d59fd565606793f090cebe3720d50dRafael J. Wysocki 36620d516602c022997feb24a9f1a806fc986b9e4e8Randy Dunlap retval = pcie_port_bus_register(); 36720d516602c022997feb24a9f1a806fc986b9e4e8Randy Dunlap if (retval) { 36820d516602c022997feb24a9f1a806fc986b9e4e8Randy Dunlap printk(KERN_WARNING "PCIE: bus_register error: %d\n", retval); 36920d516602c022997feb24a9f1a806fc986b9e4e8Randy Dunlap goto out; 37020d516602c022997feb24a9f1a806fc986b9e4e8Randy Dunlap } 3713603a6a37c5f5c6a2b109fdb48c9456f08aebdd8Sam Ravnborg retval = pci_register_driver(&pcie_portdriver); 3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (retval) 3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pcie_port_bus_unregister(); 37420d516602c022997feb24a9f1a806fc986b9e4e8Randy Dunlap out: 3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return retval; 3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(pcie_portdrv_init); 379