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