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

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;
}
143
#else
144 145
#define pcie_port_runtime_suspend	NULL
#define pcie_port_runtime_resume	NULL
146
#define pcie_port_runtime_idle		NULL
147 148
#endif

149
static const struct dev_pm_ops pcie_portdrv_pm_ops = {
150 151 152 153 154 155
	.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,
156
	.resume_noirq	= pcie_port_resume_noirq,
157 158
	.runtime_suspend = pcie_port_runtime_suspend,
	.runtime_resume = pcie_port_runtime_resume,
159
	.runtime_idle	= pcie_port_runtime_idle,
160
};
161

162
#define PCIE_PORTDRV_PM_OPS	(&pcie_portdrv_pm_ops)
163

164 165 166 167
#else /* !PM */

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

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

190
	if (!pci_is_pcie(dev) ||
191 192 193
	    ((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)))
194
		return -ENODEV;
L
Linus Torvalds 已提交
195

196
	if (!dev->irq && dev->pin) {
197
		dev_warn(&dev->dev, "device [%04x:%04x] has invalid IRQ; "
198
			 "check vendor BIOS\n", dev->vendor, dev->device);
L
Linus Torvalds 已提交
199
	}
200 201 202
	status = pcie_port_device_register(dev);
	if (status)
		return status;
L
Linus Torvalds 已提交
203

204
	pci_save_state(dev);
205 206 207 208 209
	/*
	 * D3cold may not work properly on some PCIe port, so disable
	 * it by default.
	 */
	dev->d3cold_allowed = false;
210 211
	if (!pci_match_id(port_runtime_pm_black_list, dev))
		pm_runtime_put_noidle(&dev->dev);
212

L
Linus Torvalds 已提交
213 214 215
	return 0;
}

216
static void pcie_portdrv_remove(struct pci_dev *dev)
L
Linus Torvalds 已提交
217
{
218 219
	if (!pci_match_id(port_runtime_pm_black_list, dev))
		pm_runtime_get_noresume(&dev->dev);
L
Linus Torvalds 已提交
220
	pcie_port_device_remove(dev);
221
	pci_disable_device(dev);
L
Linus Torvalds 已提交
222 223
}

224
static int error_detected_iter(struct device *device, void *data)
225
{
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
	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;
251 252
}

253 254
static pci_ers_result_t pcie_portdrv_error_detected(struct pci_dev *dev,
					enum pci_channel_state error)
255
{
256 257
	struct aer_broadcast_data data = {error, PCI_ERS_RESULT_CAN_RECOVER};
	int ret;
258

259
	/* can not fail */
260
	ret = device_for_each_child(&dev->dev, &data, error_detected_iter);
261

262
	return data.result;
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
}

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);
		}
	}
286 287 288 289

	return 0;
}

290
static pci_ers_result_t pcie_portdrv_mmio_enabled(struct pci_dev *dev)
L
Linus Torvalds 已提交
291
{
292
	pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED;
293
	int retval;
294

295 296
	/* get true return value from &status */
	retval = device_for_each_child(&dev->dev, &status, mmio_enabled_iter);
297
	return status;
L
Linus Torvalds 已提交
298 299
}

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

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

338 339
	/* get true return value from &status */
	retval = device_for_each_child(&dev->dev, &status, slot_reset_iter);
340 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)
{
366 367 368
	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 已提交
369 370 371 372 373 374 375 376 377 378 379 380
}

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

381 382 383 384 385
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,
386 387
};

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

	.probe		= pcie_portdrv_probe,
	.remove		= pcie_portdrv_remove,

395
	.err_handler 	= &pcie_portdrv_err_handler,
396 397

	.driver.pm 	= PCIE_PORTDRV_PM_OPS,
L
Linus Torvalds 已提交
398 399
};

400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
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 已提交
424 425
static int __init pcie_portdrv_init(void)
{
426
	int retval;
L
Linus Torvalds 已提交
427

428 429
	if (pcie_ports_disabled)
		return pci_register_driver(&pcie_portdriver);
430

431 432
	dmi_check_system(pcie_portdrv_dmi_table);

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

module_init(pcie_portdrv_init);