portdrv_core.c 12.8 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13
/*
 * File:	portdrv_core.c
 * Purpose:	PCI Express Port Bus Driver's Core Functions
 *
 * 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>
T
Tim Schmielau 已提交
14 15
#include <linux/string.h>
#include <linux/slab.h>
L
Linus Torvalds 已提交
16 17
#include <linux/pcieport_if.h>

18
#include "../pci.h"
L
Linus Torvalds 已提交
19 20
#include "portdrv.h"

21 22 23 24 25 26
/**
 * release_pcie_device - free PCI Express port service device structure
 * @dev: Port service device to release
 *
 * Invoked automatically when device is being removed in response to
 * device_unregister(dev).  Release all resources being claimed.
L
Linus Torvalds 已提交
27 28 29 30 31 32
 */
static void release_pcie_device(struct device *dev)
{
	kfree(to_pcie_device(dev));			
}

33 34 35 36 37 38 39 40 41
/**
 * assign_interrupt_mode - choose interrupt mode for PCI Express port services
 *                         (INTx, MSI-X, MSI) and set up vectors
 * @dev: PCI Express port to handle
 * @vectors: Array of interrupt vectors to populate
 * @mask: Bitmask of port capabilities returned by get_port_device_capability()
 *
 * Return value: Interrupt mode associated with the port
 */
L
Linus Torvalds 已提交
42 43
static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask)
{
44
	struct pcie_port_data *port_data = pci_get_drvdata(dev);
L
Linus Torvalds 已提交
45
	int i, pos, nvec, status = -EINVAL;
46
	int interrupt_mode = PCIE_PORT_NO_IRQ;
L
Linus Torvalds 已提交
47 48 49 50 51 52 53

	/* Set INTx as default */
	for (i = 0, nvec = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
		if (mask & (1 << i)) 
			nvec++;
		vectors[i] = dev->irq;
	}
54 55 56
	if (dev->pin)
		interrupt_mode = PCIE_PORT_INTx_MODE;

L
Linus Torvalds 已提交
57
	/* Check MSI quirk */
58
	if (port_data->port_type == PCIE_RC_PORT && pcie_mch_quirk)
L
Linus Torvalds 已提交
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
		return interrupt_mode;

	/* Select MSI-X over MSI if supported */		
	pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
	if (pos) {
		struct msix_entry msix_entries[PCIE_PORT_DEVICE_MAXSERVICES] = 
			{{0, 0}, {0, 1}, {0, 2}, {0, 3}};
		status = pci_enable_msix(dev, msix_entries, nvec);
		if (!status) {
			int j = 0;

			interrupt_mode = PCIE_PORT_MSIX_MODE;
			for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
				if (mask & (1 << i)) 
					vectors[i] = msix_entries[j++].vector;
			}
		}
	} 
	if (status) {
		pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
		if (pos) {
			status = pci_enable_msi(dev);
			if (!status) {
				interrupt_mode = PCIE_PORT_MSI_MODE;
				for (i = 0;i < PCIE_PORT_DEVICE_MAXSERVICES;i++)
					vectors[i] = dev->irq;
			}
		}
	} 
	return interrupt_mode;
}

91 92 93 94 95 96 97 98 99 100
/**
 * get_port_device_capability - discover capabilities of a PCI Express port
 * @dev: PCI Express port to examine
 *
 * The capabilities are read from the port's PCI Express configuration registers
 * as described in PCI Express Base Specification 1.0a sections 7.8.2, 7.8.9 and
 * 7.9 - 7.11.
 *
 * Return value: Bitmask of discovered port capabilities
 */
L
Linus Torvalds 已提交
101 102 103 104 105 106 107 108 109 110 111 112 113 114
static int get_port_device_capability(struct pci_dev *dev)
{
	int services = 0, pos;
	u16 reg16;
	u32 reg32;

	pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
	pci_read_config_word(dev, pos + PCIE_CAPABILITIES_REG, &reg16);
	/* Hot-Plug Capable */
	if (reg16 & PORT_TO_SLOT_MASK) {
		pci_read_config_dword(dev, 
			pos + PCIE_SLOT_CAPABILITIES_REG, &reg32);
		if (reg32 & SLOT_HP_CAPABLE_MASK)
			services |= PCIE_PORT_SERVICE_HP;
115 116
	}
	/* AER capable */
117 118
	if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR))
		services |= PCIE_PORT_SERVICE_AER;
119
	/* VC support */
120 121
	if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_VC))
		services |= PCIE_PORT_SERVICE_VC;
L
Linus Torvalds 已提交
122 123 124 125

	return services;
}

126 127 128 129 130 131 132 133
/**
 * pcie_device_init - initialize PCI Express port service device
 * @dev: Port service device to initialize
 * @parent: PCI Express port to associate the service device with
 * @port_type: Type of the port
 * @service_type: Type of service to associate with the service device
 * @irq: Interrupt vector to associate with the service device
 */
L
Linus Torvalds 已提交
134
static void pcie_device_init(struct pci_dev *parent, struct pcie_device *dev, 
135
	int service_type, int irq)
L
Linus Torvalds 已提交
136
{
137
	struct pcie_port_data *port_data = pci_get_drvdata(parent);
L
Linus Torvalds 已提交
138
	struct device *device;
139
	int port_type = port_data->port_type;
L
Linus Torvalds 已提交
140 141 142 143 144 145

	dev->port = parent;
	dev->irq = irq;
	dev->id.vendor = parent->vendor;
	dev->id.device = parent->device;
	dev->id.port_type = port_type;
146
	dev->id.service_type = service_type;
L
Linus Torvalds 已提交
147 148 149 150 151 152

	/* Initialize generic device interface */
	device = &dev->device;
	memset(device, 0, sizeof(struct device));
	device->bus = &pcie_port_bus_type;
	device->driver = NULL;
153
	device->driver_data = NULL;
L
Linus Torvalds 已提交
154
	device->release = release_pcie_device;	/* callback to free pcie dev */
155
	dev_set_name(device, "%s:pcie%02x",
156
		 pci_name(parent), get_descriptor_id(port_type, service_type));
L
Linus Torvalds 已提交
157 158 159
	device->parent = &parent->dev;
}

160 161 162 163 164 165 166
/**
 * alloc_pcie_device - allocate PCI Express port service device structure
 * @parent: PCI Express port to associate the service device with
 * @port_type: Type of the port
 * @service_type: Type of service to associate with the service device
 * @irq: Interrupt vector to associate with the service device
 */
167
static struct pcie_device* alloc_pcie_device(struct pci_dev *parent,
168
	int service_type, int irq)
L
Linus Torvalds 已提交
169 170 171
{
	struct pcie_device *device;

172
	device = kzalloc(sizeof(struct pcie_device), GFP_KERNEL);
L
Linus Torvalds 已提交
173 174 175
	if (!device)
		return NULL;

176
	pcie_device_init(parent, device, service_type, irq);
L
Linus Torvalds 已提交
177 178 179
	return device;
}

180 181 182 183
/**
 * pcie_port_device_probe - check if device is a PCI Express port
 * @dev: Device to check
 */
L
Linus Torvalds 已提交
184 185 186 187 188 189 190 191 192 193 194
int pcie_port_device_probe(struct pci_dev *dev)
{
	int pos, type;
	u16 reg;

	if (!(pos = pci_find_capability(dev, PCI_CAP_ID_EXP)))
		return -ENODEV;

	pci_read_config_word(dev, pos + PCIE_CAPABILITIES_REG, &reg);
	type = (reg >> 4) & PORT_TYPE_MASK;
	if (	type == PCIE_RC_PORT || type == PCIE_SW_UPSTREAM_PORT ||
195
		type == PCIE_SW_DOWNSTREAM_PORT )
L
Linus Torvalds 已提交
196
		return 0;
197

L
Linus Torvalds 已提交
198 199 200
	return -ENODEV;
}

201 202 203 204 205 206 207
/**
 * pcie_port_device_register - register PCI Express port
 * @dev: PCI Express port to register
 *
 * Allocate the port extension structure and register services associated with
 * the port.
 */
L
Linus Torvalds 已提交
208 209
int pcie_port_device_register(struct pci_dev *dev)
{
210
	struct pcie_port_data *port_data;
211
	int status, capabilities, irq_mode, i, nr_serv;
L
Linus Torvalds 已提交
212 213 214
	int vectors[PCIE_PORT_DEVICE_MAXSERVICES];
	u16 reg16;

215 216
	port_data = kzalloc(sizeof(*port_data), GFP_KERNEL);
	if (!port_data)
217
		return -ENOMEM;
218
	pci_set_drvdata(dev, port_data);
219

L
Linus Torvalds 已提交
220
	/* Get port type */
221 222
	pci_read_config_word(dev,
		pci_find_capability(dev, PCI_CAP_ID_EXP) +
L
Linus Torvalds 已提交
223
		PCIE_CAPABILITIES_REG, &reg16);
224
	port_data->port_type = (reg16 >> 4) & PORT_TYPE_MASK;
L
Linus Torvalds 已提交
225 226

	capabilities = get_port_device_capability(dev);
227 228 229 230
	/* Root ports are capable of generating PME too */
	if (port_data->port_type == PCIE_RC_PORT)
		capabilities |= PCIE_PORT_SERVICE_PME;

L
Linus Torvalds 已提交
231
	irq_mode = assign_interrupt_mode(dev, vectors, capabilities);
232 233 234 235 236 237 238 239 240 241 242
	if (irq_mode == PCIE_PORT_NO_IRQ) {
		/*
		 * Don't use service devices that require interrupts if there is
		 * no way to generate them.
		 */
		if (!(capabilities & PCIE_PORT_SERVICE_VC)) {
			status = -ENODEV;
			goto Error;
		}
		capabilities = PCIE_PORT_SERVICE_VC;
	}
243
	port_data->port_irq_mode = irq_mode;
L
Linus Torvalds 已提交
244

245 246 247 248 249
	status = pci_enable_device(dev);
	if (status)
		goto Error;
	pci_set_master(dev);

L
Linus Torvalds 已提交
250
	/* Allocate child services if any */
251
	for (i = 0, nr_serv = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
L
Linus Torvalds 已提交
252
		struct pcie_device *child;
253
		int service = 1 << i;
L
Linus Torvalds 已提交
254

255 256 257 258 259 260 261 262 263 264 265
		if (!(capabilities & service))
			continue;

		child = alloc_pcie_device(dev, service, vectors[i]);
		if (!child)
			continue;

		status = device_register(&child->device);
		if (status) {
			kfree(child);
			continue;
L
Linus Torvalds 已提交
266
		}
267 268

		get_device(&child->device);
269 270 271 272 273 274
		nr_serv++;
	}
	if (!nr_serv) {
		pci_disable_device(dev);
		status = -ENODEV;
		goto Error;
L
Linus Torvalds 已提交
275
	}
276

L
Linus Torvalds 已提交
277
	return 0;
278 279 280 281

 Error:
	kfree(port_data);
	return status;
L
Linus Torvalds 已提交
282 283 284
}

#ifdef CONFIG_PM
285
static int suspend_iter(struct device *dev, void *data)
L
Linus Torvalds 已提交
286 287
{
	struct pcie_port_service_driver *service_driver;
288
	pm_message_t state = * (pm_message_t *) data;
289 290 291 292 293 294 295 296 297

 	if ((dev->bus == &pcie_port_bus_type) &&
 	    (dev->driver)) {
 		service_driver = to_service_driver(dev->driver);
 		if (service_driver->suspend)
 			service_driver->suspend(to_pcie_device(dev), state);
  	}
	return 0;
}
L
Linus Torvalds 已提交
298

299 300 301 302 303
/**
 * pcie_port_device_suspend - suspend port services associated with a PCIe port
 * @dev: PCI Express port to handle
 * @state: Representation of system power management transition in progress
 */
304
int pcie_port_device_suspend(struct pci_dev *dev, pm_message_t state)
305
{
306
	return device_for_each_child(&dev->dev, &state, suspend_iter);
L
Linus Torvalds 已提交
307 308
}

309 310
static int resume_iter(struct device *dev, void *data)
{
L
Linus Torvalds 已提交
311 312
	struct pcie_port_service_driver *service_driver;

313 314 315 316 317
	if ((dev->bus == &pcie_port_bus_type) &&
	    (dev->driver)) {
		service_driver = to_service_driver(dev->driver);
		if (service_driver->resume)
			service_driver->resume(to_pcie_device(dev));
L
Linus Torvalds 已提交
318
	}
319 320
	return 0;
}
L
Linus Torvalds 已提交
321

322 323 324 325
/**
 * pcie_port_device_suspend - resume port services associated with a PCIe port
 * @dev: PCI Express port to handle
 */
326 327
int pcie_port_device_resume(struct pci_dev *dev)
{
328
	return device_for_each_child(&dev->dev, NULL, resume_iter);
L
Linus Torvalds 已提交
329 330 331
}
#endif

332
static int remove_iter(struct device *dev, void *data)
L
Linus Torvalds 已提交
333 334 335
{
	struct pcie_port_service_driver *service_driver;

336 337 338 339 340
	if (dev->bus == &pcie_port_bus_type) {
		if (dev->driver) {
			service_driver = to_service_driver(dev->driver);
			if (service_driver->remove)
				service_driver->remove(to_pcie_device(dev));
L
Linus Torvalds 已提交
341
		}
342 343
		*(unsigned long*)data = (unsigned long)dev;
		return 1;
L
Linus Torvalds 已提交
344
	}
345 346 347
	return 0;
}

348 349 350 351 352 353 354
/**
 * pcie_port_device_remove - unregister PCI Express port service devices
 * @dev: PCI Express port the service devices to unregister are associated with
 *
 * Remove PCI Express port service devices associated with given port and
 * disable MSI-X or MSI for the port.
 */
355 356
void pcie_port_device_remove(struct pci_dev *dev)
{
357
	struct pcie_port_data *port_data = pci_get_drvdata(dev);
358 359 360
	int status;

	do {
361 362
		unsigned long device_addr;

363 364
		status = device_for_each_child(&dev->dev, &device_addr, remove_iter);
		if (status) {
365
			struct device *device = (struct device*)device_addr;
366 367 368 369
			put_device(device);
			device_unregister(device);
		}
	} while (status);
370 371 372

	switch (port_data->port_irq_mode) {
	case PCIE_PORT_MSIX_MODE:
L
Linus Torvalds 已提交
373
		pci_disable_msix(dev);
374 375
		break;
	case PCIE_PORT_MSI_MODE:
L
Linus Torvalds 已提交
376
		pci_disable_msi(dev);
377 378 379 380
		break;
	}

	kfree(port_data);
L
Linus Torvalds 已提交
381 382
}

383 384 385 386 387 388 389 390
/**
 * pcie_port_probe_service - probe driver for given PCI Express port service
 * @dev: PCI Express port service device to probe against
 *
 * If PCI Express port service driver is registered with
 * pcie_port_service_register(), this function will be called by the driver core
 * whenever match is found between the driver and a port service device.
 */
391
static int pcie_port_probe_service(struct device *dev)
L
Linus Torvalds 已提交
392
{
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411
	struct pcie_device *pciedev;
	struct pcie_port_service_driver *driver;
	int status;

	if (!dev || !dev->driver)
		return -ENODEV;

	driver = to_service_driver(dev->driver);
	if (!driver || !driver->probe)
		return -ENODEV;

	pciedev = to_pcie_device(dev);
	status = driver->probe(pciedev, driver->id_table);
	if (!status) {
		dev_printk(KERN_DEBUG, dev, "service driver %s loaded\n",
			driver->name);
		get_device(dev);
	}
	return status;
L
Linus Torvalds 已提交
412 413
}

414 415 416 417 418 419 420 421 422
/**
 * pcie_port_remove_service - detach driver from given PCI Express port service
 * @dev: PCI Express port service device to handle
 *
 * If PCI Express port service driver is registered with
 * pcie_port_service_register(), this function will be called by the driver core
 * when device_unregister() is called for the port service device associated
 * with the driver.
 */
423
static int pcie_port_remove_service(struct device *dev)
L
Linus Torvalds 已提交
424
{
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
	struct pcie_device *pciedev;
	struct pcie_port_service_driver *driver;

	if (!dev || !dev->driver)
		return 0;

	pciedev = to_pcie_device(dev);
	driver = to_service_driver(dev->driver);
	if (driver && driver->remove) {
		dev_printk(KERN_DEBUG, dev, "unloading service driver %s\n",
			driver->name);
		driver->remove(pciedev);
		put_device(dev);
	}
	return 0;
L
Linus Torvalds 已提交
440 441
}

442 443 444 445 446 447 448 449 450
/**
 * pcie_port_shutdown_service - shut down given PCI Express port service
 * @dev: PCI Express port service device to handle
 *
 * If PCI Express port service driver is registered with
 * pcie_port_service_register(), this function will be called by the driver core
 * when device_shutdown() is called for the port service device associated
 * with the driver.
 */
451 452
static void pcie_port_shutdown_service(struct device *dev) {}

453 454 455 456
/**
 * pcie_port_service_register - register PCI Express port service driver
 * @new: PCI Express port service driver to register
 */
L
Linus Torvalds 已提交
457 458 459 460 461 462 463 464 465
int pcie_port_service_register(struct pcie_port_service_driver *new)
{
	new->driver.name = (char *)new->name;
	new->driver.bus = &pcie_port_bus_type;
	new->driver.probe = pcie_port_probe_service;
	new->driver.remove = pcie_port_remove_service;
	new->driver.shutdown = pcie_port_shutdown_service;

	return driver_register(&new->driver);
466
}
L
Linus Torvalds 已提交
467

468 469 470 471
/**
 * pcie_port_service_unregister - unregister PCI Express port service driver
 * @drv: PCI Express port service driver to unregister
 */
472
void pcie_port_service_unregister(struct pcie_port_service_driver *drv)
L
Linus Torvalds 已提交
473
{
474
	driver_unregister(&drv->driver);
L
Linus Torvalds 已提交
475 476 477 478
}

EXPORT_SYMBOL(pcie_port_service_register);
EXPORT_SYMBOL(pcie_port_service_unregister);