portdrv_pci.c 10.7 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13
/*
 * File:	portdrv_pci.c
 * Purpose:	PCI Express Port Bus Driver
 *
 * Copyright (C) 2004 Intel
 * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
 */

#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/pm.h>
14
#include <linux/pm_runtime.h>
L
Linus Torvalds 已提交
15 16
#include <linux/init.h>
#include <linux/pcieport_if.h>
17
#include <linux/aer.h>
18
#include <linux/dmi.h>
19
#include <linux/pci-aspm.h>
L
Linus Torvalds 已提交
20 21

#include "portdrv.h"
22
#include "aer/aerdrv.h"
L
Linus Torvalds 已提交
23 24 25 26 27 28

/*
 * Version Information
 */
#define DRIVER_VERSION "v1.0"
#define DRIVER_AUTHOR "tom.l.nguyen@intel.com"
29
#define DRIVER_DESC "PCIe Port Bus Driver"
L
Linus Torvalds 已提交
30 31 32 33
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

34 35 36
/* If this switch is set, PCIe port native services should not be enabled. */
bool pcie_ports_disabled;

37 38 39 40 41 42
/*
 * If this switch is set, ACPI _OSC will be used to determine whether or not to
 * enable PCIe port native services.
 */
bool pcie_ports_auto = true;

43 44
static int __init pcie_port_setup(char *str)
{
45
	if (!strncmp(str, "compat", 6)) {
46
		pcie_ports_disabled = true;
47 48 49 50 51 52 53
	} else if (!strncmp(str, "native", 6)) {
		pcie_ports_disabled = false;
		pcie_ports_auto = false;
	} else if (!strncmp(str, "auto", 4)) {
		pcie_ports_disabled = false;
		pcie_ports_auto = true;
	}
54 55 56 57 58

	return 1;
}
__setup("pcie_ports=", pcie_port_setup);

L
Linus Torvalds 已提交
59 60
/* global data */

61 62 63 64 65 66
/**
 * pcie_clear_root_pme_status - Clear root port PME interrupt status.
 * @dev: PCIe root port or event collector.
 */
void pcie_clear_root_pme_status(struct pci_dev *dev)
{
67
	pcie_capability_set_dword(dev, PCI_EXP_RTSTA, PCI_EXP_RTSTA_PME);
68 69
}

70 71 72 73 74 75 76 77 78 79 80
static int pcie_portdrv_restore_config(struct pci_dev *dev)
{
	int retval;

	retval = pci_enable_device(dev);
	if (retval)
		return retval;
	pci_set_master(dev);
	return 0;
}

81
#ifdef CONFIG_PM
82 83 84 85 86 87 88 89 90
static int pcie_port_resume_noirq(struct device *dev)
{
	struct pci_dev *pdev = to_pci_dev(dev);

	/*
	 * Some BIOSes forget to clear Root PME Status bits after system wakeup
	 * which breaks ACPI-based runtime wakeup on PCI Express, so clear those
	 * bits now just in case (shouldn't hurt).
	 */
91
	if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT)
92 93 94 95
		pcie_clear_root_pme_status(pdev);
	return 0;
}

96
#ifdef CONFIG_PM_RUNTIME
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
struct d3cold_info {
	bool no_d3cold;
	unsigned int d3cold_delay;
};

static int pci_dev_d3cold_info(struct pci_dev *pdev, void *data)
{
	struct d3cold_info *info = data;

	info->d3cold_delay = max_t(unsigned int, pdev->d3cold_delay,
				   info->d3cold_delay);
	if (pdev->no_d3cold)
		info->no_d3cold = true;
	return 0;
}

static int pcie_port_runtime_suspend(struct device *dev)
{
	struct pci_dev *pdev = to_pci_dev(dev);
	struct d3cold_info d3cold_info = {
		.no_d3cold	= false,
		.d3cold_delay	= PCI_PM_D3_WAIT,
	};

	/*
	 * If any subordinate device disable D3cold, we should not put
	 * the port into D3cold.  The D3cold delay of port should be
	 * the max of that of all subordinate devices.
	 */
	pci_walk_bus(pdev->subordinate, pci_dev_d3cold_info, &d3cold_info);
	pdev->no_d3cold = d3cold_info.no_d3cold;
	pdev->d3cold_delay = d3cold_info.d3cold_delay;
	return 0;
}

static int pcie_port_runtime_resume(struct device *dev)
133 134 135
{
	return 0;
}
136

137 138 139 140 141 142 143 144 145
static int pci_dev_pme_poll(struct pci_dev *pdev, void *data)
{
	bool *pme_poll = data;

	if (pdev->pme_poll)
		*pme_poll = true;
	return 0;
}

146 147
static int pcie_port_runtime_idle(struct device *dev)
{
148 149 150 151 152 153 154 155
	struct pci_dev *pdev = to_pci_dev(dev);
	bool pme_poll = false;

	/*
	 * If any subordinate device needs pme poll, we should keep
	 * the port in D0, because we need port in D0 to poll it.
	 */
	pci_walk_bus(pdev->subordinate, pci_dev_pme_poll, &pme_poll);
156
	/* Delay for a short while to prevent too frequent suspend/resume */
157 158
	if (!pme_poll)
		pm_schedule_suspend(dev, 10);
159 160
	return -EBUSY;
}
161
#else
162 163
#define pcie_port_runtime_suspend	NULL
#define pcie_port_runtime_resume	NULL
164
#define pcie_port_runtime_idle		NULL
165 166
#endif

167
static const struct dev_pm_ops pcie_portdrv_pm_ops = {
168 169 170 171 172 173
	.suspend	= pcie_port_device_suspend,
	.resume		= pcie_port_device_resume,
	.freeze		= pcie_port_device_suspend,
	.thaw		= pcie_port_device_resume,
	.poweroff	= pcie_port_device_suspend,
	.restore	= pcie_port_device_resume,
174
	.resume_noirq	= pcie_port_resume_noirq,
175 176
	.runtime_suspend = pcie_port_runtime_suspend,
	.runtime_resume = pcie_port_runtime_resume,
177
	.runtime_idle	= pcie_port_runtime_idle,
178
};
179

180
#define PCIE_PORTDRV_PM_OPS	(&pcie_portdrv_pm_ops)
181

182 183 184 185
#else /* !PM */

#define PCIE_PORTDRV_PM_OPS	NULL
#endif /* !PM */
186

L
Linus Torvalds 已提交
187 188 189 190
/*
 * pcie_portdrv_probe - Probe PCI-Express port devices
 * @dev: PCI-Express port device being probed
 *
191
 * If detected invokes the pcie_port_device_register() method for
L
Linus Torvalds 已提交
192 193 194
 * this port device.
 *
 */
B
Bill Pemberton 已提交
195
static int pcie_portdrv_probe(struct pci_dev *dev,
196
					const struct pci_device_id *id)
L
Linus Torvalds 已提交
197
{
198
	int status;
L
Linus Torvalds 已提交
199

200
	if (!pci_is_pcie(dev) ||
201 202 203
	    ((pci_pcie_type(dev) != PCI_EXP_TYPE_ROOT_PORT) &&
	     (pci_pcie_type(dev) != PCI_EXP_TYPE_UPSTREAM) &&
	     (pci_pcie_type(dev) != PCI_EXP_TYPE_DOWNSTREAM)))
204
		return -ENODEV;
L
Linus Torvalds 已提交
205

206
	if (!dev->irq && dev->pin) {
207
		dev_warn(&dev->dev, "device [%04x:%04x] has invalid IRQ; "
208
			 "check vendor BIOS\n", dev->vendor, dev->device);
L
Linus Torvalds 已提交
209
	}
210 211 212
	status = pcie_port_device_register(dev);
	if (status)
		return status;
L
Linus Torvalds 已提交
213

214
	pci_save_state(dev);
215 216 217 218 219
	/*
	 * D3cold may not work properly on some PCIe port, so disable
	 * it by default.
	 */
	dev->d3cold_allowed = false;
L
Linus Torvalds 已提交
220 221 222
	return 0;
}

223
static void pcie_portdrv_remove(struct pci_dev *dev)
L
Linus Torvalds 已提交
224 225
{
	pcie_port_device_remove(dev);
226
	pci_disable_device(dev);
L
Linus Torvalds 已提交
227 228
}

229
static int error_detected_iter(struct device *device, void *data)
230
{
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
	struct pcie_device *pcie_device;
	struct pcie_port_service_driver *driver;
	struct aer_broadcast_data *result_data;
	pci_ers_result_t status;

	result_data = (struct aer_broadcast_data *) data;

	if (device->bus == &pcie_port_bus_type && device->driver) {
		driver = to_service_driver(device->driver);
		if (!driver ||
			!driver->err_handler ||
			!driver->err_handler->error_detected)
			return 0;

		pcie_device = to_pcie_device(device);

		/* Forward error detected message to service drivers */
		status = driver->err_handler->error_detected(
			pcie_device->port,
			result_data->state);
		result_data->result =
			merge_result(result_data->result, status);
	}

	return 0;
256 257
}

258 259
static pci_ers_result_t pcie_portdrv_error_detected(struct pci_dev *dev,
					enum pci_channel_state error)
260
{
261
	struct aer_broadcast_data data = {error, PCI_ERS_RESULT_CAN_RECOVER};
262

B
Bjorn Helgaas 已提交
263 264
	/* get true return value from &data */
	device_for_each_child(&dev->dev, &data, error_detected_iter);
265
	return data.result;
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
}

static int mmio_enabled_iter(struct device *device, void *data)
{
	struct pcie_device *pcie_device;
	struct pcie_port_service_driver *driver;
	pci_ers_result_t status, *result;

	result = (pci_ers_result_t *) data;

	if (device->bus == &pcie_port_bus_type && device->driver) {
		driver = to_service_driver(device->driver);
		if (driver &&
			driver->err_handler &&
			driver->err_handler->mmio_enabled) {
			pcie_device = to_pcie_device(device);

			/* Forward error message to service drivers */
			status = driver->err_handler->mmio_enabled(
					pcie_device->port);
			*result = merge_result(*result, status);
		}
	}
289 290 291 292

	return 0;
}

293
static pci_ers_result_t pcie_portdrv_mmio_enabled(struct pci_dev *dev)
L
Linus Torvalds 已提交
294
{
295
	pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED;
296

297
	/* get true return value from &status */
B
Bjorn Helgaas 已提交
298
	device_for_each_child(&dev->dev, &status, mmio_enabled_iter);
299
	return status;
L
Linus Torvalds 已提交
300 301
}

302
static int slot_reset_iter(struct device *device, void *data)
L
Linus Torvalds 已提交
303
{
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
	struct pcie_device *pcie_device;
	struct pcie_port_service_driver *driver;
	pci_ers_result_t status, *result;

	result = (pci_ers_result_t *) data;

	if (device->bus == &pcie_port_bus_type && device->driver) {
		driver = to_service_driver(device->driver);
		if (driver &&
			driver->err_handler &&
			driver->err_handler->slot_reset) {
			pcie_device = to_pcie_device(device);

			/* Forward error message to service drivers */
			status = driver->err_handler->slot_reset(
					pcie_device->port);
			*result = merge_result(*result, status);
		}
	}

	return 0;
}

static pci_ers_result_t pcie_portdrv_slot_reset(struct pci_dev *dev)
{
329
	pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED;
330 331 332

	/* If fatal, restore cfg space for possible link reset at upstream */
	if (dev->error_state == pci_channel_io_frozen) {
333
		dev->state_saved = true;
334
		pci_restore_state(dev);
335 336 337 338
		pcie_portdrv_restore_config(dev);
		pci_enable_pcie_error_reporting(dev);
	}

339
	/* get true return value from &status */
B
Bjorn Helgaas 已提交
340
	device_for_each_child(&dev->dev, &status, slot_reset_iter);
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
	return status;
}

static int resume_iter(struct device *device, void *data)
{
	struct pcie_device *pcie_device;
	struct pcie_port_service_driver *driver;

	if (device->bus == &pcie_port_bus_type && device->driver) {
		driver = to_service_driver(device->driver);
		if (driver &&
			driver->err_handler &&
			driver->err_handler->resume) {
			pcie_device = to_pcie_device(device);

			/* Forward error message to service drivers */
			driver->err_handler->resume(pcie_device->port);
		}
	}

	return 0;
}

static void pcie_portdrv_err_resume(struct pci_dev *dev)
{
B
Bjorn Helgaas 已提交
366
	device_for_each_child(&dev->dev, NULL, resume_iter);
L
Linus Torvalds 已提交
367 368 369 370 371 372 373 374 375 376 377 378
}

/*
 * LINUX Device Driver Model
 */
static const struct pci_device_id port_pci_ids[] = { {
	/* handle any PCI-Express port */
	PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ~0),
	}, { /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE(pci, port_pci_ids);

379 380 381 382 383
static const struct pci_error_handlers pcie_portdrv_err_handler = {
	.error_detected = pcie_portdrv_error_detected,
	.mmio_enabled = pcie_portdrv_mmio_enabled,
	.slot_reset = pcie_portdrv_slot_reset,
	.resume = pcie_portdrv_err_resume,
384 385
};

386
static struct pci_driver pcie_portdriver = {
387
	.name		= "pcieport",
L
Linus Torvalds 已提交
388 389 390 391 392
	.id_table	= &port_pci_ids[0],

	.probe		= pcie_portdrv_probe,
	.remove		= pcie_portdrv_remove,

393
	.err_handler 	= &pcie_portdrv_err_handler,
394 395

	.driver.pm 	= PCIE_PORTDRV_PM_OPS,
L
Linus Torvalds 已提交
396 397
};

398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
static int __init dmi_pcie_pme_disable_msi(const struct dmi_system_id *d)
{
	pr_notice("%s detected: will not use MSI for PCIe PME signaling\n",
			d->ident);
	pcie_pme_disable_msi();
	return 0;
}

static struct dmi_system_id __initdata pcie_portdrv_dmi_table[] = {
	/*
	 * Boxes that should not use MSI for PCIe PME signaling.
	 */
	{
	 .callback = dmi_pcie_pme_disable_msi,
	 .ident = "MSI Wind U-100",
	 .matches = {
		     DMI_MATCH(DMI_SYS_VENDOR,
		     		"MICRO-STAR INTERNATIONAL CO., LTD"),
		     DMI_MATCH(DMI_PRODUCT_NAME, "U-100"),
		     },
	 },
	 {}
};

L
Linus Torvalds 已提交
422 423
static int __init pcie_portdrv_init(void)
{
424
	int retval;
L
Linus Torvalds 已提交
425

426 427
	if (pcie_ports_disabled)
		return pci_register_driver(&pcie_portdriver);
428

429 430
	dmi_check_system(pcie_portdrv_dmi_table);

431 432 433 434 435
	retval = pcie_port_bus_register();
	if (retval) {
		printk(KERN_WARNING "PCIE: bus_register error: %d\n", retval);
		goto out;
	}
436
	retval = pci_register_driver(&pcie_portdriver);
L
Linus Torvalds 已提交
437 438
	if (retval)
		pcie_port_bus_unregister();
439
 out:
L
Linus Torvalds 已提交
440 441 442 443
	return retval;
}

module_init(pcie_portdrv_init);