msi.c 17.2 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8
/*
 * File:	msi.c
 * Purpose:	PCI Message Signaled Interrupt (MSI)
 *
 * Copyright (C) 2003-2004 Intel
 * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
 */

9
#include <linux/err.h>
L
Linus Torvalds 已提交
10 11 12 13 14 15 16 17
#include <linux/mm.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/smp_lock.h>
#include <linux/pci.h>
#include <linux/proc_fs.h>
18
#include <linux/msi.h>
L
Linus Torvalds 已提交
19 20 21 22 23 24 25 26 27 28

#include <asm/errno.h>
#include <asm/io.h>
#include <asm/smp.h>

#include "pci.h"
#include "msi.h"

static int pci_msi_enable = 1;

29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
static void msi_set_enable(struct pci_dev *dev, int enable)
{
	int pos;
	u16 control;

	pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
	if (pos) {
		pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control);
		control &= ~PCI_MSI_FLAGS_ENABLE;
		if (enable)
			control |= PCI_MSI_FLAGS_ENABLE;
		pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control);
	}
}

static void msix_set_enable(struct pci_dev *dev, int enable)
{
	int pos;
	u16 control;

	pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
	if (pos) {
		pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control);
		control &= ~PCI_MSIX_FLAGS_ENABLE;
		if (enable)
			control |= PCI_MSIX_FLAGS_ENABLE;
		pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control);
	}
}

M
Mitch Williams 已提交
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
static void msix_flush_writes(unsigned int irq)
{
	struct msi_desc *entry;

	entry = get_irq_msi(irq);
	BUG_ON(!entry || !entry->dev);
	switch (entry->msi_attrib.type) {
	case PCI_CAP_ID_MSI:
		/* nothing to do */
		break;
	case PCI_CAP_ID_MSIX:
	{
		int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE +
			PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET;
		readl(entry->mask_base + offset);
		break;
	}
	default:
		BUG();
		break;
	}
}

82
static void msi_set_mask_bit(unsigned int irq, int flag)
L
Linus Torvalds 已提交
83 84 85
{
	struct msi_desc *entry;

86
	entry = get_irq_msi(irq);
87
	BUG_ON(!entry || !entry->dev);
L
Linus Torvalds 已提交
88 89
	switch (entry->msi_attrib.type) {
	case PCI_CAP_ID_MSI:
90
		if (entry->msi_attrib.maskbit) {
S
Satoru Takeuchi 已提交
91 92
			int pos;
			u32 mask_bits;
93 94 95 96 97 98

			pos = (long)entry->mask_base;
			pci_read_config_dword(entry->dev, pos, &mask_bits);
			mask_bits &= ~(1);
			mask_bits |= flag;
			pci_write_config_dword(entry->dev, pos, mask_bits);
99 100
		} else {
			msi_set_enable(entry->dev, !flag);
101
		}
L
Linus Torvalds 已提交
102 103 104 105 106 107
		break;
	case PCI_CAP_ID_MSIX:
	{
		int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE +
			PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET;
		writel(flag, entry->mask_base + offset);
108
		readl(entry->mask_base + offset);
L
Linus Torvalds 已提交
109 110 111
		break;
	}
	default:
112
		BUG();
L
Linus Torvalds 已提交
113 114
		break;
	}
115
	entry->msi_attrib.masked = !!flag;
L
Linus Torvalds 已提交
116 117
}

118
void read_msi_msg(unsigned int irq, struct msi_msg *msg)
L
Linus Torvalds 已提交
119
{
120
	struct msi_desc *entry = get_irq_msi(irq);
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
	switch(entry->msi_attrib.type) {
	case PCI_CAP_ID_MSI:
	{
		struct pci_dev *dev = entry->dev;
		int pos = entry->msi_attrib.pos;
		u16 data;

		pci_read_config_dword(dev, msi_lower_address_reg(pos),
					&msg->address_lo);
		if (entry->msi_attrib.is_64) {
			pci_read_config_dword(dev, msi_upper_address_reg(pos),
						&msg->address_hi);
			pci_read_config_word(dev, msi_data_reg(pos, 1), &data);
		} else {
			msg->address_hi = 0;
			pci_read_config_word(dev, msi_data_reg(pos, 1), &data);
		}
		msg->data = data;
		break;
	}
	case PCI_CAP_ID_MSIX:
	{
		void __iomem *base;
		base = entry->mask_base +
			entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE;

		msg->address_lo = readl(base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET);
		msg->address_hi = readl(base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET);
		msg->data = readl(base + PCI_MSIX_ENTRY_DATA_OFFSET);
 		break;
 	}
 	default:
		BUG();
	}
}
L
Linus Torvalds 已提交
156

157
void write_msi_msg(unsigned int irq, struct msi_msg *msg)
158
{
159
	struct msi_desc *entry = get_irq_msi(irq);
L
Linus Torvalds 已提交
160 161 162
	switch (entry->msi_attrib.type) {
	case PCI_CAP_ID_MSI:
	{
163 164 165 166 167 168 169 170 171 172 173 174 175 176
		struct pci_dev *dev = entry->dev;
		int pos = entry->msi_attrib.pos;

		pci_write_config_dword(dev, msi_lower_address_reg(pos),
					msg->address_lo);
		if (entry->msi_attrib.is_64) {
			pci_write_config_dword(dev, msi_upper_address_reg(pos),
						msg->address_hi);
			pci_write_config_word(dev, msi_data_reg(pos, 1),
						msg->data);
		} else {
			pci_write_config_word(dev, msi_data_reg(pos, 0),
						msg->data);
		}
L
Linus Torvalds 已提交
177 178 179 180
		break;
	}
	case PCI_CAP_ID_MSIX:
	{
181 182 183 184 185 186 187 188 189
		void __iomem *base;
		base = entry->mask_base +
			entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE;

		writel(msg->address_lo,
			base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET);
		writel(msg->address_hi,
			base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET);
		writel(msg->data, base + PCI_MSIX_ENTRY_DATA_OFFSET);
L
Linus Torvalds 已提交
190 191 192
		break;
	}
	default:
193
		BUG();
L
Linus Torvalds 已提交
194
	}
195
	entry->msg = *msg;
L
Linus Torvalds 已提交
196
}
197

198
void mask_msi_irq(unsigned int irq)
L
Linus Torvalds 已提交
199
{
200
	msi_set_mask_bit(irq, 1);
M
Mitch Williams 已提交
201
	msix_flush_writes(irq);
L
Linus Torvalds 已提交
202 203
}

204
void unmask_msi_irq(unsigned int irq)
L
Linus Torvalds 已提交
205
{
206
	msi_set_mask_bit(irq, 0);
M
Mitch Williams 已提交
207
	msix_flush_writes(irq);
L
Linus Torvalds 已提交
208 209
}

210
static int msi_free_irq(struct pci_dev* dev, int irq);
S
Satoru Takeuchi 已提交
211

L
Linus Torvalds 已提交
212 213 214 215 216

static struct msi_desc* alloc_msi_entry(void)
{
	struct msi_desc *entry;

M
Michael Ellerman 已提交
217
	entry = kzalloc(sizeof(struct msi_desc), GFP_KERNEL);
L
Linus Torvalds 已提交
218 219 220 221 222 223 224 225 226
	if (!entry)
		return NULL;

	entry->link.tail = entry->link.head = 0;	/* single message */
	entry->dev = NULL;

	return entry;
}

227
#ifdef CONFIG_PM
228
static void __pci_restore_msi_state(struct pci_dev *dev)
229
{
230
	int pos;
231
	u16 control;
232
	struct msi_desc *entry;
233

234 235 236
	if (!dev->msi_enabled)
		return;

237 238
	entry = get_irq_msi(dev->irq);
	pos = entry->msi_attrib.pos;
239

240 241
	pci_intx(dev, 0);		/* disable intx */
	msi_set_enable(dev, 0);
242 243 244 245 246 247 248 249
	write_msi_msg(dev->irq, &entry->msg);
	if (entry->msi_attrib.maskbit)
		msi_set_mask_bit(dev->irq, entry->msi_attrib.masked);

	pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control);
	control &= ~(PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE);
	if (entry->msi_attrib.maskbit || !entry->msi_attrib.masked)
		control |= PCI_MSI_FLAGS_ENABLE;
250
	pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control);
251 252 253
}

static void __pci_restore_msix_state(struct pci_dev *dev)
254 255
{
	int pos;
256
	int irq, head, tail = 0;
257
	struct msi_desc *entry;
258
	u16 control;
259

E
Eric W. Biederman 已提交
260 261 262
	if (!dev->msix_enabled)
		return;

263
	/* route the table */
264 265
	pci_intx(dev, 0);		/* disable intx */
	msix_set_enable(dev, 0);
E
Eric W. Biederman 已提交
266
	irq = head = dev->first_msi_irq;
267 268
	entry = get_irq_msi(irq);
	pos = entry->msi_attrib.pos;
269
	while (head != tail) {
270
		entry = get_irq_msi(irq);
271 272
		write_msi_msg(irq, &entry->msg);
		msi_set_mask_bit(irq, entry->msi_attrib.masked);
273

274
		tail = entry->link.tail;
275
		irq = tail;
276 277
	}

278 279 280 281
	pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control);
	control &= ~PCI_MSIX_FLAGS_MASKALL;
	control |= PCI_MSIX_FLAGS_ENABLE;
	pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control);
282
}
283 284 285 286 287 288

void pci_restore_msi_state(struct pci_dev *dev)
{
	__pci_restore_msi_state(dev);
	__pci_restore_msix_state(dev);
}
S
Satoru Takeuchi 已提交
289
#endif	/* CONFIG_PM */
290

L
Linus Torvalds 已提交
291 292 293 294
/**
 * msi_capability_init - configure device's MSI capability structure
 * @dev: pointer to the pci_dev data structure of MSI device function
 *
295
 * Setup the MSI capability structure of device function with a single
296
 * MSI irq, regardless of device function is capable of handling
L
Linus Torvalds 已提交
297
 * multiple messages. A return of zero indicates the successful setup
298
 * of an entry zero with the new MSI irq or non-zero for otherwise.
L
Linus Torvalds 已提交
299 300 301 302
 **/
static int msi_capability_init(struct pci_dev *dev)
{
	struct msi_desc *entry;
303
	int pos, irq;
L
Linus Torvalds 已提交
304 305
	u16 control;

306 307
	msi_set_enable(dev, 0);	/* Ensure msi is disabled as I set it up */

L
Linus Torvalds 已提交
308 309 310
   	pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
	pci_read_config_word(dev, msi_control_reg(pos), &control);
	/* MSI Entry Initialization */
311 312 313
	entry = alloc_msi_entry();
	if (!entry)
		return -ENOMEM;
314

L
Linus Torvalds 已提交
315
	entry->msi_attrib.type = PCI_CAP_ID_MSI;
316
	entry->msi_attrib.is_64 = is_64bit_address(control);
L
Linus Torvalds 已提交
317 318
	entry->msi_attrib.entry_nr = 0;
	entry->msi_attrib.maskbit = is_mask_bit_support(control);
319
	entry->msi_attrib.masked = 1;
320
	entry->msi_attrib.default_irq = dev->irq;	/* Save IOAPIC IRQ */
321
	entry->msi_attrib.pos = pos;
L
Linus Torvalds 已提交
322 323 324 325
	if (is_mask_bit_support(control)) {
		entry->mask_base = (void __iomem *)(long)msi_mask_bits_reg(pos,
				is_64bit_address(control));
	}
326 327 328 329 330 331 332 333 334 335 336 337 338 339
	entry->dev = dev;
	if (entry->msi_attrib.maskbit) {
		unsigned int maskbits, temp;
		/* All MSIs are unmasked by default, Mask them all */
		pci_read_config_dword(dev,
			msi_mask_bits_reg(pos, is_64bit_address(control)),
			&maskbits);
		temp = (1 << multi_msi_capable(control));
		temp = ((temp - 1) & ~temp);
		maskbits |= temp;
		pci_write_config_dword(dev,
			msi_mask_bits_reg(pos, is_64bit_address(control)),
			maskbits);
	}
L
Linus Torvalds 已提交
340
	/* Configure MSI capability structure */
341 342
	irq = arch_setup_msi_irq(dev, entry);
	if (irq < 0) {
M
Michael Ellerman 已提交
343
		kfree(entry);
344
		return irq;
345
	}
346 347
	entry->link.head = irq;
	entry->link.tail = irq;
E
Eric W. Biederman 已提交
348
	dev->first_msi_irq = irq;
349
	set_irq_msi(irq, entry);
350

L
Linus Torvalds 已提交
351
	/* Set MSI enabled bits	 */
352 353 354
	pci_intx(dev, 0);		/* disable intx */
	msi_set_enable(dev, 1);
	dev->msi_enabled = 1;
L
Linus Torvalds 已提交
355

356
	dev->irq = irq;
L
Linus Torvalds 已提交
357 358 359 360 361 362
	return 0;
}

/**
 * msix_capability_init - configure device's MSI-X capability
 * @dev: pointer to the pci_dev data structure of MSI-X device function
R
Randy Dunlap 已提交
363 364
 * @entries: pointer to an array of struct msix_entry entries
 * @nvec: number of @entries
L
Linus Torvalds 已提交
365
 *
366
 * Setup the MSI-X capability structure of device function with a
367 368
 * single MSI-X irq. A return of zero indicates the successful setup of
 * requested MSI-X entries with allocated irqs or non-zero for otherwise.
L
Linus Torvalds 已提交
369 370 371 372 373
 **/
static int msix_capability_init(struct pci_dev *dev,
				struct msix_entry *entries, int nvec)
{
	struct msi_desc *head = NULL, *tail = NULL, *entry = NULL;
374
	int irq, pos, i, j, nr_entries, temp = 0;
375 376
	unsigned long phys_addr;
	u32 table_offset;
L
Linus Torvalds 已提交
377 378 379 380
 	u16 control;
	u8 bir;
	void __iomem *base;

381 382
	msix_set_enable(dev, 0);/* Ensure msix is disabled as I set it up */

L
Linus Torvalds 已提交
383 384 385 386
   	pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
	/* Request & Map MSI-X table region */
 	pci_read_config_word(dev, msi_control_reg(pos), &control);
	nr_entries = multi_msix_capable(control);
387 388

 	pci_read_config_dword(dev, msix_table_offset_reg(pos), &table_offset);
L
Linus Torvalds 已提交
389
	bir = (u8)(table_offset & PCI_MSIX_FLAGS_BIRMASK);
390 391
	table_offset &= ~PCI_MSIX_FLAGS_BIRMASK;
	phys_addr = pci_resource_start (dev, bir) + table_offset;
L
Linus Torvalds 已提交
392 393 394 395 396 397
	base = ioremap_nocache(phys_addr, nr_entries * PCI_MSIX_ENTRY_SIZE);
	if (base == NULL)
		return -ENOMEM;

	/* MSI-X Table Initialization */
	for (i = 0; i < nvec; i++) {
398 399
		entry = alloc_msi_entry();
		if (!entry)
L
Linus Torvalds 已提交
400 401 402 403
			break;

 		j = entries[i].entry;
		entry->msi_attrib.type = PCI_CAP_ID_MSIX;
404
		entry->msi_attrib.is_64 = 1;
L
Linus Torvalds 已提交
405 406
		entry->msi_attrib.entry_nr = j;
		entry->msi_attrib.maskbit = 1;
407
		entry->msi_attrib.masked = 1;
408
		entry->msi_attrib.default_irq = dev->irq;
409
		entry->msi_attrib.pos = pos;
L
Linus Torvalds 已提交
410 411
		entry->dev = dev;
		entry->mask_base = base;
412 413 414 415

		/* Configure MSI-X capability structure */
		irq = arch_setup_msi_irq(dev, entry);
		if (irq < 0) {
M
Michael Ellerman 已提交
416
			kfree(entry);
417 418 419
			break;
		}
 		entries[i].vector = irq;
L
Linus Torvalds 已提交
420
		if (!head) {
421 422
			entry->link.head = irq;
			entry->link.tail = irq;
L
Linus Torvalds 已提交
423 424 425 426
			head = entry;
		} else {
			entry->link.head = temp;
			entry->link.tail = tail->link.tail;
427 428
			tail->link.tail = irq;
			head->link.head = irq;
L
Linus Torvalds 已提交
429
		}
430
		temp = irq;
L
Linus Torvalds 已提交
431
		tail = entry;
432

433
		set_irq_msi(irq, entry);
L
Linus Torvalds 已提交
434 435
	}
	if (i != nvec) {
436
		int avail = i - 1;
L
Linus Torvalds 已提交
437 438
		i--;
		for (; i >= 0; i--) {
439 440
			irq = (entries + i)->vector;
			msi_free_irq(dev, irq);
L
Linus Torvalds 已提交
441 442
			(entries + i)->vector = 0;
		}
443 444 445 446 447 448
		/* If we had some success report the number of irqs
		 * we succeeded in setting up.
		 */
		if (avail <= 0)
			avail = -EBUSY;
		return avail;
L
Linus Torvalds 已提交
449
	}
E
Eric W. Biederman 已提交
450
	dev->first_msi_irq = entries[0].vector;
L
Linus Torvalds 已提交
451
	/* Set MSI-X enabled bits */
452 453 454
	pci_intx(dev, 0);		/* disable intx */
	msix_set_enable(dev, 1);
	dev->msix_enabled = 1;
L
Linus Torvalds 已提交
455 456 457 458

	return 0;
}

459 460 461
/**
 * pci_msi_supported - check whether MSI may be enabled on device
 * @dev: pointer to the pci_dev data structure of MSI device function
462
 * @type: are we checking for MSI or MSI-X ?
463
 *
464 465
 * Look at global flags, the device itself, and its parent busses
 * to return 0 if MSI are supported for the device.
466 467
 **/
static
468
int pci_msi_supported(struct pci_dev * dev, int type)
469 470 471
{
	struct pci_bus *bus;

472
	/* MSI must be globally enabled and supported by the device */
473 474 475
	if (!pci_msi_enable || !dev || dev->no_msi)
		return -EINVAL;

476 477 478 479 480 481
	/* Any bridge which does NOT route MSI transactions from it's
	 * secondary bus to it's primary bus must set NO_MSI flag on
	 * the secondary pci_bus.
	 * We expect only arch-specific PCI host bus controller driver
	 * or quirks for specific PCI bridges to be setting NO_MSI.
	 */
482 483 484 485
	for (bus = dev->bus; bus; bus = bus->parent)
		if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI)
			return -EINVAL;

486 487 488
	if (!pci_find_capability(dev, type))
		return -EINVAL;

489 490 491
	return 0;
}

L
Linus Torvalds 已提交
492 493 494 495 496
/**
 * pci_enable_msi - configure device's MSI capability structure
 * @dev: pointer to the pci_dev data structure of MSI device function
 *
 * Setup the MSI capability structure of device function with
497
 * a single MSI irq upon its software driver call to request for
L
Linus Torvalds 已提交
498 499
 * MSI mode enabled on its hardware device function. A return of zero
 * indicates the successful setup of an entry zero with the new MSI
500
 * irq or non-zero for otherwise.
L
Linus Torvalds 已提交
501 502 503
 **/
int pci_enable_msi(struct pci_dev* dev)
{
504
	int status;
L
Linus Torvalds 已提交
505

506
	if (pci_msi_supported(dev, PCI_CAP_ID_MSI))
L
Linus Torvalds 已提交
507 508
		return -EINVAL;

E
Eric W. Biederman 已提交
509
	WARN_ON(!!dev->msi_enabled);
L
Linus Torvalds 已提交
510

511
	/* Check whether driver already requested for MSI-X irqs */
512 513 514 515 516
	if (dev->msix_enabled) {
		printk(KERN_INFO "PCI: %s: Can't enable MSI.  "
			"Device already has MSI-X enabled\n",
			pci_name(dev));
		return -EINVAL;
L
Linus Torvalds 已提交
517 518 519 520
	}
	status = msi_capability_init(dev);
	return status;
}
521
EXPORT_SYMBOL(pci_enable_msi);
L
Linus Torvalds 已提交
522 523 524 525

void pci_disable_msi(struct pci_dev* dev)
{
	struct msi_desc *entry;
526
	int default_irq;
L
Linus Torvalds 已提交
527

528
	if (!pci_msi_enable || !dev || !dev->msi_enabled)
E
Eric W. Biederman 已提交
529 530
		return;

531 532 533
	msi_set_enable(dev, 0);
	pci_intx(dev, 1);		/* enable intx */
	dev->msi_enabled = 0;
534

535
	entry = get_irq_msi(dev->first_msi_irq);
L
Linus Torvalds 已提交
536 537 538
	if (!entry || !entry->dev || entry->msi_attrib.type != PCI_CAP_ID_MSI) {
		return;
	}
539 540 541 542 543 544 545

	default_irq = entry->msi_attrib.default_irq;
	msi_free_irq(dev, dev->first_msi_irq);

	/* Restore dev->irq to its default pin-assertion irq */
	dev->irq = default_irq;

E
Eric W. Biederman 已提交
546
	dev->first_msi_irq = 0;
L
Linus Torvalds 已提交
547
}
548
EXPORT_SYMBOL(pci_disable_msi);
L
Linus Torvalds 已提交
549

550
static int msi_free_irq(struct pci_dev* dev, int irq)
L
Linus Torvalds 已提交
551 552 553 554 555
{
	struct msi_desc *entry;
	int head, entry_nr, type;
	void __iomem *base;

M
Michael Ellerman 已提交
556 557
	BUG_ON(irq_has_action(irq));

558
	entry = get_irq_msi(irq);
L
Linus Torvalds 已提交
559 560 561 562 563 564 565
	if (!entry || entry->dev != dev) {
		return -EINVAL;
	}
	type = entry->msi_attrib.type;
	entry_nr = entry->msi_attrib.entry_nr;
	head = entry->link.head;
	base = entry->mask_base;
566 567
	get_irq_msi(entry->link.head)->link.tail = entry->link.tail;
	get_irq_msi(entry->link.tail)->link.head = entry->link.head;
L
Linus Torvalds 已提交
568

569
	arch_teardown_msi_irq(irq);
M
Michael Ellerman 已提交
570
	kfree(entry);
L
Linus Torvalds 已提交
571 572

	if (type == PCI_CAP_ID_MSIX) {
573 574
		writel(1, base + entry_nr * PCI_MSIX_ENTRY_SIZE +
			PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
L
Linus Torvalds 已提交
575

576
		if (head == irq)
L
Linus Torvalds 已提交
577 578 579 580 581 582 583 584 585
			iounmap(base);
	}

	return 0;
}

/**
 * pci_enable_msix - configure device's MSI-X capability structure
 * @dev: pointer to the pci_dev data structure of MSI-X device function
586
 * @entries: pointer to an array of MSI-X entries
587
 * @nvec: number of MSI-X irqs requested for allocation by device driver
L
Linus Torvalds 已提交
588 589
 *
 * Setup the MSI-X capability structure of device function with the number
590
 * of requested irqs upon its software driver call to request for
L
Linus Torvalds 已提交
591 592
 * MSI-X mode enabled on its hardware device function. A return of zero
 * indicates the successful configuration of MSI-X capability structure
593
 * with new allocated MSI-X irqs. A return of < 0 indicates a failure.
L
Linus Torvalds 已提交
594
 * Or a return of > 0 indicates that driver request is exceeding the number
595
 * of irqs available. Driver should use the returned value to re-send
L
Linus Torvalds 已提交
596 597 598 599
 * its request.
 **/
int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec)
{
600
	int status, pos, nr_entries;
E
Eric W. Biederman 已提交
601
	int i, j;
L
Linus Torvalds 已提交
602 603
	u16 control;

604
	if (!entries || pci_msi_supported(dev, PCI_CAP_ID_MSIX))
L
Linus Torvalds 已提交
605 606
 		return -EINVAL;

607
	pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
L
Linus Torvalds 已提交
608 609 610 611 612 613 614 615 616 617 618 619 620 621
	pci_read_config_word(dev, msi_control_reg(pos), &control);
	nr_entries = multi_msix_capable(control);
	if (nvec > nr_entries)
		return -EINVAL;

	/* Check for any invalid entries */
	for (i = 0; i < nvec; i++) {
		if (entries[i].entry >= nr_entries)
			return -EINVAL;		/* invalid entry */
		for (j = i + 1; j < nvec; j++) {
			if (entries[i].entry == entries[j].entry)
				return -EINVAL;	/* duplicate entry */
		}
	}
E
Eric W. Biederman 已提交
622
	WARN_ON(!!dev->msix_enabled);
623

624
	/* Check whether driver already requested for MSI irq */
625
   	if (dev->msi_enabled) {
L
Linus Torvalds 已提交
626
		printk(KERN_INFO "PCI: %s: Can't enable MSI-X.  "
627
		       "Device already has an MSI irq assigned\n",
L
Linus Torvalds 已提交
628 629 630 631 632 633
		       pci_name(dev));
		return -EINVAL;
	}
	status = msix_capability_init(dev, entries, nvec);
	return status;
}
634
EXPORT_SYMBOL(pci_enable_msix);
L
Linus Torvalds 已提交
635

636
static void msix_free_all_irqs(struct pci_dev *dev)
L
Linus Torvalds 已提交
637
{
638
	int irq, head, tail = 0;
L
Linus Torvalds 已提交
639

640 641 642 643 644 645 646 647 648 649 650 651 652 653
	irq = head = dev->first_msi_irq;
	while (head != tail) {
		tail = get_irq_msi(irq)->link.tail;

		if (irq != head)
			msi_free_irq(dev, irq);
		irq = tail;
	}
	msi_free_irq(dev, irq);
	dev->first_msi_irq = 0;
}

void pci_disable_msix(struct pci_dev* dev)
{
654
	if (!pci_msi_enable || !dev || !dev->msix_enabled)
E
Eric W. Biederman 已提交
655 656
		return;

657 658 659
	msix_set_enable(dev, 0);
	pci_intx(dev, 1);		/* enable intx */
	dev->msix_enabled = 0;
660

661
	msix_free_all_irqs(dev);
L
Linus Torvalds 已提交
662
}
663
EXPORT_SYMBOL(pci_disable_msix);
L
Linus Torvalds 已提交
664 665

/**
666
 * msi_remove_pci_irq_vectors - reclaim MSI(X) irqs to unused state
L
Linus Torvalds 已提交
667 668
 * @dev: pointer to the pci_dev data structure of MSI(X) device function
 *
669
 * Being called during hotplug remove, from which the device function
670
 * is hot-removed. All previous assigned MSI/MSI-X irqs, if
L
Linus Torvalds 已提交
671 672 673 674 675 676 677 678
 * allocated for this device function, are reclaimed to unused state,
 * which may be used later on.
 **/
void msi_remove_pci_irq_vectors(struct pci_dev* dev)
{
	if (!pci_msi_enable || !dev)
 		return;

M
Michael Ellerman 已提交
679
	if (dev->msi_enabled)
680
		msi_free_irq(dev, dev->first_msi_irq);
L
Linus Torvalds 已提交
681

682 683
	if (dev->msix_enabled)
		msix_free_all_irqs(dev);
L
Linus Torvalds 已提交
684 685
}

686 687 688 689
void pci_no_msi(void)
{
	pci_msi_enable = 0;
}