portdrv_pci.c 11.2 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

187 188 189 190 191 192 193 194
/*
 * PCIe port runtime suspend is broken for some chipsets, so use a
 * black list to disable runtime PM for these chipsets.
 */
static const struct pci_device_id port_runtime_pm_black_list[] = {
	{ /* end: all zeroes */ }
};

L
Linus Torvalds 已提交
195 196 197 198
/*
 * pcie_portdrv_probe - Probe PCI-Express port devices
 * @dev: PCI-Express port device being probed
 *
199
 * If detected invokes the pcie_port_device_register() method for
L
Linus Torvalds 已提交
200 201 202
 * this port device.
 *
 */
B
Bill Pemberton 已提交
203
static int pcie_portdrv_probe(struct pci_dev *dev,
204
					const struct pci_device_id *id)
L
Linus Torvalds 已提交
205
{
206
	int status;
L
Linus Torvalds 已提交
207

208
	if (!pci_is_pcie(dev) ||
209 210 211
	    ((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)))
212
		return -ENODEV;
L
Linus Torvalds 已提交
213

214
	if (!dev->irq && dev->pin) {
215
		dev_warn(&dev->dev, "device [%04x:%04x] has invalid IRQ; "
216
			 "check vendor BIOS\n", dev->vendor, dev->device);
L
Linus Torvalds 已提交
217
	}
218 219 220
	status = pcie_port_device_register(dev);
	if (status)
		return status;
L
Linus Torvalds 已提交
221

222
	pci_save_state(dev);
223 224 225 226 227
	/*
	 * D3cold may not work properly on some PCIe port, so disable
	 * it by default.
	 */
	dev->d3cold_allowed = false;
228 229
	if (!pci_match_id(port_runtime_pm_black_list, dev))
		pm_runtime_put_noidle(&dev->dev);
230

L
Linus Torvalds 已提交
231 232 233
	return 0;
}

234
static void pcie_portdrv_remove(struct pci_dev *dev)
L
Linus Torvalds 已提交
235
{
236 237
	if (!pci_match_id(port_runtime_pm_black_list, dev))
		pm_runtime_get_noresume(&dev->dev);
L
Linus Torvalds 已提交
238
	pcie_port_device_remove(dev);
239
	pci_disable_device(dev);
L
Linus Torvalds 已提交
240 241
}

242
static int error_detected_iter(struct device *device, void *data)
243
{
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
	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;
269 270
}

271 272
static pci_ers_result_t pcie_portdrv_error_detected(struct pci_dev *dev,
					enum pci_channel_state error)
273
{
274 275
	struct aer_broadcast_data data = {error, PCI_ERS_RESULT_CAN_RECOVER};
	int ret;
276

277
	/* can not fail */
278
	ret = device_for_each_child(&dev->dev, &data, error_detected_iter);
279

280
	return data.result;
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
}

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);
		}
	}
304 305 306 307

	return 0;
}

308
static pci_ers_result_t pcie_portdrv_mmio_enabled(struct pci_dev *dev)
L
Linus Torvalds 已提交
309
{
310
	pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED;
311
	int retval;
312

313 314
	/* get true return value from &status */
	retval = device_for_each_child(&dev->dev, &status, mmio_enabled_iter);
315
	return status;
L
Linus Torvalds 已提交
316 317
}

318
static int slot_reset_iter(struct device *device, void *data)
L
Linus Torvalds 已提交
319
{
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
	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)
{
345
	pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED;
346
	int retval;
347 348 349

	/* If fatal, restore cfg space for possible link reset at upstream */
	if (dev->error_state == pci_channel_io_frozen) {
350
		dev->state_saved = true;
351
		pci_restore_state(dev);
352 353 354 355
		pcie_portdrv_restore_config(dev);
		pci_enable_pcie_error_reporting(dev);
	}

356 357
	/* get true return value from &status */
	retval = device_for_each_child(&dev->dev, &status, slot_reset_iter);
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383

	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)
{
384 385 386
	int retval;
	/* nothing to do with error value, if it ever happens */
	retval = device_for_each_child(&dev->dev, NULL, resume_iter);
L
Linus Torvalds 已提交
387 388 389 390 391 392 393 394 395 396 397 398
}

/*
 * 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);

399 400 401 402 403
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,
404 405
};

406
static struct pci_driver pcie_portdriver = {
407
	.name		= "pcieport",
L
Linus Torvalds 已提交
408 409 410 411 412
	.id_table	= &port_pci_ids[0],

	.probe		= pcie_portdrv_probe,
	.remove		= pcie_portdrv_remove,

413
	.err_handler 	= &pcie_portdrv_err_handler,
414 415

	.driver.pm 	= PCIE_PORTDRV_PM_OPS,
L
Linus Torvalds 已提交
416 417
};

418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
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 已提交
442 443
static int __init pcie_portdrv_init(void)
{
444
	int retval;
L
Linus Torvalds 已提交
445

446 447
	if (pcie_ports_disabled)
		return pci_register_driver(&pcie_portdriver);
448

449 450
	dmi_check_system(pcie_portdrv_dmi_table);

451 452 453 454 455
	retval = pcie_port_bus_register();
	if (retval) {
		printk(KERN_WARNING "PCIE: bus_register error: %d\n", retval);
		goto out;
	}
456
	retval = pci_register_driver(&pcie_portdriver);
L
Linus Torvalds 已提交
457 458
	if (retval)
		pcie_port_bus_unregister();
459
 out:
L
Linus Torvalds 已提交
460 461 462 463
	return retval;
}

module_init(pcie_portdrv_init);