portdrv_pci.c 10.9 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 67 68 69 70 71 72 73 74 75 76
/**
 * 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)
{
	int rtsta_pos;
	u32 rtsta;

	rtsta_pos = pci_pcie_cap(dev) + PCI_EXP_RTSTA;

	pci_read_config_dword(dev, rtsta_pos, &rtsta);
	rtsta |= PCI_EXP_RTSTA_PME;
	pci_write_config_dword(dev, rtsta_pos, rtsta);
}

77 78 79 80 81 82 83 84 85 86 87
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;
}

88
#ifdef CONFIG_PM
89 90 91 92 93 94 95 96 97 98 99 100 101 102
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).
	 */
	if(pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
		pcie_clear_root_pme_status(pdev);
	return 0;
}

103
#ifdef CONFIG_PM_RUNTIME
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 133 134 135 136 137 138 139
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)
140 141 142
{
	return 0;
}
143 144 145 146 147 148 149

static int pcie_port_runtime_idle(struct device *dev)
{
	/* Delay for a short while to prevent too frequent suspend/resume */
	pm_schedule_suspend(dev, 10);
	return -EBUSY;
}
150
#else
151 152
#define pcie_port_runtime_suspend	NULL
#define pcie_port_runtime_resume	NULL
153
#define pcie_port_runtime_idle		NULL
154 155
#endif

156
static const struct dev_pm_ops pcie_portdrv_pm_ops = {
157 158 159 160 161 162
	.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,
163
	.resume_noirq	= pcie_port_resume_noirq,
164 165
	.runtime_suspend = pcie_port_runtime_suspend,
	.runtime_resume = pcie_port_runtime_resume,
166
	.runtime_idle	= pcie_port_runtime_idle,
167
};
168

169
#define PCIE_PORTDRV_PM_OPS	(&pcie_portdrv_pm_ops)
170

171 172 173 174
#else /* !PM */

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

176 177 178 179 180 181 182 183
/*
 * 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 已提交
184 185 186 187
/*
 * pcie_portdrv_probe - Probe PCI-Express port devices
 * @dev: PCI-Express port device being probed
 *
188
 * If detected invokes the pcie_port_device_register() method for
L
Linus Torvalds 已提交
189 190 191
 * this port device.
 *
 */
192 193
static int __devinit pcie_portdrv_probe(struct pci_dev *dev,
					const struct pci_device_id *id)
L
Linus Torvalds 已提交
194
{
195
	int status;
L
Linus Torvalds 已提交
196

197 198 199 200 201
	if (!pci_is_pcie(dev) ||
	    ((dev->pcie_type != PCI_EXP_TYPE_ROOT_PORT) &&
	     (dev->pcie_type != PCI_EXP_TYPE_UPSTREAM) &&
	     (dev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM)))
		return -ENODEV;
L
Linus Torvalds 已提交
202

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

211
	pci_save_state(dev);
212 213 214 215 216
	/*
	 * D3cold may not work properly on some PCIe port, so disable
	 * it by default.
	 */
	dev->d3cold_allowed = false;
217 218
	if (!pci_match_id(port_runtime_pm_black_list, dev))
		pm_runtime_put_noidle(&dev->dev);
219

L
Linus Torvalds 已提交
220 221 222
	return 0;
}

223
static void pcie_portdrv_remove(struct pci_dev *dev)
L
Linus Torvalds 已提交
224
{
225 226
	if (!pci_match_id(port_runtime_pm_black_list, dev))
		pm_runtime_get_noresume(&dev->dev);
L
Linus Torvalds 已提交
227
	pcie_port_device_remove(dev);
228
	pci_disable_device(dev);
L
Linus Torvalds 已提交
229 230
}

231
static int error_detected_iter(struct device *device, void *data)
232
{
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
	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;
258 259
}

260 261
static pci_ers_result_t pcie_portdrv_error_detected(struct pci_dev *dev,
					enum pci_channel_state error)
262
{
263 264
	struct aer_broadcast_data data = {error, PCI_ERS_RESULT_CAN_RECOVER};
	int ret;
265

266
	/* can not fail */
267
	ret = device_for_each_child(&dev->dev, &data, error_detected_iter);
268

269
	return data.result;
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
}

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);
		}
	}
293 294 295 296

	return 0;
}

297
static pci_ers_result_t pcie_portdrv_mmio_enabled(struct pci_dev *dev)
L
Linus Torvalds 已提交
298
{
299
	pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED;
300
	int retval;
301

302 303
	/* get true return value from &status */
	retval = device_for_each_child(&dev->dev, &status, mmio_enabled_iter);
304
	return status;
L
Linus Torvalds 已提交
305 306
}

307
static int slot_reset_iter(struct device *device, void *data)
L
Linus Torvalds 已提交
308
{
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
	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)
{
334
	pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED;
335
	int retval;
336 337 338

	/* If fatal, restore cfg space for possible link reset at upstream */
	if (dev->error_state == pci_channel_io_frozen) {
339
		dev->state_saved = true;
340
		pci_restore_state(dev);
341 342 343 344
		pcie_portdrv_restore_config(dev);
		pci_enable_pcie_error_reporting(dev);
	}

345 346
	/* get true return value from &status */
	retval = device_for_each_child(&dev->dev, &status, slot_reset_iter);
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372

	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)
{
373 374 375
	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 已提交
376 377 378 379 380 381 382 383 384 385 386 387
}

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

388 389 390 391 392 393 394
static 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,
};

395
static struct pci_driver pcie_portdriver = {
396
	.name		= "pcieport",
L
Linus Torvalds 已提交
397 398 399 400 401
	.id_table	= &port_pci_ids[0],

	.probe		= pcie_portdrv_probe,
	.remove		= pcie_portdrv_remove,

402
	.err_handler 	= &pcie_portdrv_err_handler,
403 404

	.driver.pm 	= PCIE_PORTDRV_PM_OPS,
L
Linus Torvalds 已提交
405 406
};

407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
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 已提交
431 432
static int __init pcie_portdrv_init(void)
{
433
	int retval;
L
Linus Torvalds 已提交
434

435 436
	if (pcie_ports_disabled)
		return pci_register_driver(&pcie_portdriver);
437

438 439
	dmi_check_system(pcie_portdrv_dmi_table);

440 441 442 443 444
	retval = pcie_port_bus_register();
	if (retval) {
		printk(KERN_WARNING "PCIE: bus_register error: %d\n", retval);
		goto out;
	}
445
	retval = pci_register_driver(&pcie_portdriver);
L
Linus Torvalds 已提交
446 447
	if (retval)
		pcie_port_bus_unregister();
448
 out:
L
Linus Torvalds 已提交
449 450 451 452
	return retval;
}

module_init(pcie_portdrv_init);