msi.c 20.8 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

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

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

27
static struct kmem_cache* msi_cachep;
L
Linus Torvalds 已提交
28 29 30 31 32

static int pci_msi_enable = 1;

static int msi_cache_init(void)
{
33 34
	msi_cachep = kmem_cache_create("msi_cache", sizeof(struct msi_desc),
					0, SLAB_HWCACHE_ALIGN, NULL, NULL);
L
Linus Torvalds 已提交
35 36 37 38 39 40
	if (!msi_cachep)
		return -ENOMEM;

	return 0;
}

41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
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);
	}
}

71
static void msi_set_mask_bit(unsigned int irq, int flag)
L
Linus Torvalds 已提交
72 73 74
{
	struct msi_desc *entry;

75
	entry = get_irq_msi(irq);
76
	BUG_ON(!entry || !entry->dev);
L
Linus Torvalds 已提交
77 78
	switch (entry->msi_attrib.type) {
	case PCI_CAP_ID_MSI:
79
		if (entry->msi_attrib.maskbit) {
S
Satoru Takeuchi 已提交
80 81
			int pos;
			u32 mask_bits;
82 83 84 85 86 87 88

			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);
		}
L
Linus Torvalds 已提交
89 90 91 92 93 94 95 96 97
		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);
		break;
	}
	default:
98
		BUG();
L
Linus Torvalds 已提交
99 100 101 102
		break;
	}
}

103
void read_msi_msg(unsigned int irq, struct msi_msg *msg)
L
Linus Torvalds 已提交
104
{
105
	struct msi_desc *entry = get_irq_msi(irq);
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 140
	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 已提交
141

142
void write_msi_msg(unsigned int irq, struct msi_msg *msg)
143
{
144
	struct msi_desc *entry = get_irq_msi(irq);
L
Linus Torvalds 已提交
145 146 147
	switch (entry->msi_attrib.type) {
	case PCI_CAP_ID_MSI:
	{
148 149 150 151 152 153 154 155 156 157 158 159 160 161
		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 已提交
162 163 164 165
		break;
	}
	case PCI_CAP_ID_MSIX:
	{
166 167 168 169 170 171 172 173 174
		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 已提交
175 176 177
		break;
	}
	default:
178
		BUG();
L
Linus Torvalds 已提交
179 180
	}
}
181

182
void mask_msi_irq(unsigned int irq)
L
Linus Torvalds 已提交
183
{
184
	msi_set_mask_bit(irq, 1);
L
Linus Torvalds 已提交
185 186
}

187
void unmask_msi_irq(unsigned int irq)
L
Linus Torvalds 已提交
188
{
189
	msi_set_mask_bit(irq, 0);
L
Linus Torvalds 已提交
190 191
}

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

L
Linus Torvalds 已提交
194 195 196 197 198 199 200
static int msi_init(void)
{
	static int status = -ENOMEM;

	if (!status)
		return status;

201 202
	status = msi_cache_init();
	if (status < 0) {
L
Linus Torvalds 已提交
203 204 205 206
		pci_msi_enable = 0;
		printk(KERN_WARNING "PCI: MSI cache init failed\n");
		return status;
	}
207

L
Linus Torvalds 已提交
208 209 210 211 212 213 214
	return status;
}

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

215
	entry = kmem_cache_zalloc(msi_cachep, GFP_KERNEL);
L
Linus Torvalds 已提交
216 217 218 219 220 221 222 223 224
	if (!entry)
		return NULL;

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

	return entry;
}

225
#ifdef CONFIG_PM
226
static int __pci_save_msi_state(struct pci_dev *dev)
227 228 229 230 231 232
{
	int pos, i = 0;
	u16 control;
	struct pci_cap_saved_state *save_state;
	u32 *cap;

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

236 237
	pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
	if (pos <= 0)
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
		return 0;

	save_state = kzalloc(sizeof(struct pci_cap_saved_state) + sizeof(u32) * 5,
		GFP_KERNEL);
	if (!save_state) {
		printk(KERN_ERR "Out of memory in pci_save_msi_state\n");
		return -ENOMEM;
	}
	cap = &save_state->data[0];

	pci_read_config_dword(dev, pos, &cap[i++]);
	control = cap[0] >> 16;
	pci_read_config_dword(dev, pos + PCI_MSI_ADDRESS_LO, &cap[i++]);
	if (control & PCI_MSI_FLAGS_64BIT) {
		pci_read_config_dword(dev, pos + PCI_MSI_ADDRESS_HI, &cap[i++]);
		pci_read_config_dword(dev, pos + PCI_MSI_DATA_64, &cap[i++]);
	} else
		pci_read_config_dword(dev, pos + PCI_MSI_DATA_32, &cap[i++]);
	if (control & PCI_MSI_FLAGS_MASKBIT)
		pci_read_config_dword(dev, pos + PCI_MSI_MASK_BIT, &cap[i++]);
	save_state->cap_nr = PCI_CAP_ID_MSI;
	pci_add_saved_cap(dev, save_state);
	return 0;
}

263
static void __pci_restore_msi_state(struct pci_dev *dev)
264 265 266 267 268 269
{
	int i = 0, pos;
	u16 control;
	struct pci_cap_saved_state *save_state;
	u32 *cap;

270 271 272
	if (!dev->msi_enabled)
		return;

273 274 275 276 277 278
	save_state = pci_find_saved_cap(dev, PCI_CAP_ID_MSI);
	pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
	if (!save_state || pos <= 0)
		return;
	cap = &save_state->data[0];

279
	pci_intx(dev, 0);		/* disable intx */
280
	control = cap[i++] >> 16;
281
	msi_set_enable(dev, 0);
282 283 284 285 286 287 288 289 290 291 292 293 294
	pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_LO, cap[i++]);
	if (control & PCI_MSI_FLAGS_64BIT) {
		pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_HI, cap[i++]);
		pci_write_config_dword(dev, pos + PCI_MSI_DATA_64, cap[i++]);
	} else
		pci_write_config_dword(dev, pos + PCI_MSI_DATA_32, cap[i++]);
	if (control & PCI_MSI_FLAGS_MASKBIT)
		pci_write_config_dword(dev, pos + PCI_MSI_MASK_BIT, cap[i++]);
	pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control);
	pci_remove_saved_cap(save_state);
	kfree(save_state);
}

295
static int __pci_save_msix_state(struct pci_dev *dev)
296 297
{
	int pos;
298
	int irq, head, tail = 0;
299 300 301
	u16 control;
	struct pci_cap_saved_state *save_state;

E
Eric W. Biederman 已提交
302 303 304
	if (!dev->msix_enabled)
		return 0;

305
	pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
306
	if (pos <= 0)
307 308
		return 0;

309
	/* save the capability */
310 311 312 313 314 315 316 317 318
	pci_read_config_word(dev, msi_control_reg(pos), &control);
	save_state = kzalloc(sizeof(struct pci_cap_saved_state) + sizeof(u16),
		GFP_KERNEL);
	if (!save_state) {
		printk(KERN_ERR "Out of memory in pci_save_msix_state\n");
		return -ENOMEM;
	}
	*((u16 *)&save_state->data[0]) = control;

319
	/* save the table */
E
Eric W. Biederman 已提交
320
	irq = head = dev->first_msi_irq;
321 322 323
	while (head != tail) {
		struct msi_desc *entry;

324
		entry = get_irq_msi(irq);
325
		read_msi_msg(irq, &entry->msg_save);
326

327
		tail = entry->link.tail;
328
		irq = tail;
329 330
	}

331 332 333 334 335
	save_state->cap_nr = PCI_CAP_ID_MSIX;
	pci_add_saved_cap(dev, save_state);
	return 0;
}

336 337 338 339 340 341 342 343 344 345 346 347 348 349
int pci_save_msi_state(struct pci_dev *dev)
{
	int rc;

	rc = __pci_save_msi_state(dev);
	if (rc)
		return rc;

	rc = __pci_save_msix_state(dev);

	return rc;
}

static void __pci_restore_msix_state(struct pci_dev *dev)
350 351 352
{
	u16 save;
	int pos;
353
	int irq, head, tail = 0;
354 355 356
	struct msi_desc *entry;
	struct pci_cap_saved_state *save_state;

E
Eric W. Biederman 已提交
357 358 359
	if (!dev->msix_enabled)
		return;

360 361 362 363 364 365 366 367 368 369 370 371
	save_state = pci_find_saved_cap(dev, PCI_CAP_ID_MSIX);
	if (!save_state)
		return;
	save = *((u16 *)&save_state->data[0]);
	pci_remove_saved_cap(save_state);
	kfree(save_state);

	pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
	if (pos <= 0)
		return;

	/* route the table */
372 373
	pci_intx(dev, 0);		/* disable intx */
	msix_set_enable(dev, 0);
E
Eric W. Biederman 已提交
374
	irq = head = dev->first_msi_irq;
375
	while (head != tail) {
376
		entry = get_irq_msi(irq);
377
		write_msi_msg(irq, &entry->msg_save);
378

379
		tail = entry->link.tail;
380
		irq = tail;
381 382 383 384
	}

	pci_write_config_word(dev, msi_control_reg(pos), save);
}
385 386 387 388 389 390

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

L
Linus Torvalds 已提交
393 394 395 396
/**
 * msi_capability_init - configure device's MSI capability structure
 * @dev: pointer to the pci_dev data structure of MSI device function
 *
397
 * Setup the MSI capability structure of device function with a single
398
 * MSI irq, regardless of device function is capable of handling
L
Linus Torvalds 已提交
399
 * multiple messages. A return of zero indicates the successful setup
400
 * of an entry zero with the new MSI irq or non-zero for otherwise.
L
Linus Torvalds 已提交
401 402 403 404
 **/
static int msi_capability_init(struct pci_dev *dev)
{
	struct msi_desc *entry;
405
	int pos, irq;
L
Linus Torvalds 已提交
406 407
	u16 control;

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

L
Linus Torvalds 已提交
410 411 412
   	pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
	pci_read_config_word(dev, msi_control_reg(pos), &control);
	/* MSI Entry Initialization */
413 414 415
	entry = alloc_msi_entry();
	if (!entry)
		return -ENOMEM;
416

L
Linus Torvalds 已提交
417
	entry->msi_attrib.type = PCI_CAP_ID_MSI;
418
	entry->msi_attrib.is_64 = is_64bit_address(control);
L
Linus Torvalds 已提交
419 420
	entry->msi_attrib.entry_nr = 0;
	entry->msi_attrib.maskbit = is_mask_bit_support(control);
421
	entry->msi_attrib.default_irq = dev->irq;	/* Save IOAPIC IRQ */
422
	entry->msi_attrib.pos = pos;
L
Linus Torvalds 已提交
423 424 425 426
	if (is_mask_bit_support(control)) {
		entry->mask_base = (void __iomem *)(long)msi_mask_bits_reg(pos,
				is_64bit_address(control));
	}
427 428 429 430 431 432 433 434 435 436 437 438 439 440
	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 已提交
441
	/* Configure MSI capability structure */
442 443 444 445
	irq = arch_setup_msi_irq(dev, entry);
	if (irq < 0) {
		kmem_cache_free(msi_cachep, entry);
		return irq;
446
	}
447 448
	entry->link.head = irq;
	entry->link.tail = irq;
E
Eric W. Biederman 已提交
449
	dev->first_msi_irq = irq;
450
	set_irq_msi(irq, entry);
451

L
Linus Torvalds 已提交
452
	/* Set MSI enabled bits	 */
453 454 455
	pci_intx(dev, 0);		/* disable intx */
	msi_set_enable(dev, 1);
	dev->msi_enabled = 1;
L
Linus Torvalds 已提交
456

457
	dev->irq = irq;
L
Linus Torvalds 已提交
458 459 460 461 462 463
	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 已提交
464 465
 * @entries: pointer to an array of struct msix_entry entries
 * @nvec: number of @entries
L
Linus Torvalds 已提交
466
 *
467
 * Setup the MSI-X capability structure of device function with a
468 469
 * 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 已提交
470 471 472 473 474
 **/
static int msix_capability_init(struct pci_dev *dev,
				struct msix_entry *entries, int nvec)
{
	struct msi_desc *head = NULL, *tail = NULL, *entry = NULL;
475
	int irq, pos, i, j, nr_entries, temp = 0;
476 477
	unsigned long phys_addr;
	u32 table_offset;
L
Linus Torvalds 已提交
478 479 480 481
 	u16 control;
	u8 bir;
	void __iomem *base;

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

L
Linus Torvalds 已提交
484 485 486 487
   	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);
488 489

 	pci_read_config_dword(dev, msix_table_offset_reg(pos), &table_offset);
L
Linus Torvalds 已提交
490
	bir = (u8)(table_offset & PCI_MSIX_FLAGS_BIRMASK);
491 492
	table_offset &= ~PCI_MSIX_FLAGS_BIRMASK;
	phys_addr = pci_resource_start (dev, bir) + table_offset;
L
Linus Torvalds 已提交
493 494 495 496 497 498
	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++) {
499 500
		entry = alloc_msi_entry();
		if (!entry)
L
Linus Torvalds 已提交
501 502 503 504
			break;

 		j = entries[i].entry;
		entry->msi_attrib.type = PCI_CAP_ID_MSIX;
505
		entry->msi_attrib.is_64 = 1;
L
Linus Torvalds 已提交
506 507
		entry->msi_attrib.entry_nr = j;
		entry->msi_attrib.maskbit = 1;
508
		entry->msi_attrib.default_irq = dev->irq;
509
		entry->msi_attrib.pos = pos;
L
Linus Torvalds 已提交
510 511
		entry->dev = dev;
		entry->mask_base = base;
512 513 514 515 516 517 518 519

		/* Configure MSI-X capability structure */
		irq = arch_setup_msi_irq(dev, entry);
		if (irq < 0) {
			kmem_cache_free(msi_cachep, entry);
			break;
		}
 		entries[i].vector = irq;
L
Linus Torvalds 已提交
520
		if (!head) {
521 522
			entry->link.head = irq;
			entry->link.tail = irq;
L
Linus Torvalds 已提交
523 524 525 526
			head = entry;
		} else {
			entry->link.head = temp;
			entry->link.tail = tail->link.tail;
527 528
			tail->link.tail = irq;
			head->link.head = irq;
L
Linus Torvalds 已提交
529
		}
530
		temp = irq;
L
Linus Torvalds 已提交
531
		tail = entry;
532

533
		set_irq_msi(irq, entry);
L
Linus Torvalds 已提交
534 535
	}
	if (i != nvec) {
536
		int avail = i - 1;
L
Linus Torvalds 已提交
537 538
		i--;
		for (; i >= 0; i--) {
539 540
			irq = (entries + i)->vector;
			msi_free_irq(dev, irq);
L
Linus Torvalds 已提交
541 542
			(entries + i)->vector = 0;
		}
543 544 545 546 547 548
		/* 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 已提交
549
	}
E
Eric W. Biederman 已提交
550
	dev->first_msi_irq = entries[0].vector;
L
Linus Torvalds 已提交
551
	/* Set MSI-X enabled bits */
552 553 554
	pci_intx(dev, 0);		/* disable intx */
	msix_set_enable(dev, 1);
	dev->msix_enabled = 1;
L
Linus Torvalds 已提交
555 556 557 558

	return 0;
}

559 560 561 562
/**
 * pci_msi_supported - check whether MSI may be enabled on device
 * @dev: pointer to the pci_dev data structure of MSI device function
 *
563 564
 * Look at global flags, the device itself, and its parent busses
 * to return 0 if MSI are supported for the device.
565 566 567 568 569 570
 **/
static
int pci_msi_supported(struct pci_dev * dev)
{
	struct pci_bus *bus;

571
	/* MSI must be globally enabled and supported by the device */
572 573 574
	if (!pci_msi_enable || !dev || dev->no_msi)
		return -EINVAL;

575 576 577 578 579 580
	/* 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.
	 */
581 582 583 584 585 586 587
	for (bus = dev->bus; bus; bus = bus->parent)
		if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI)
			return -EINVAL;

	return 0;
}

L
Linus Torvalds 已提交
588 589 590 591 592
/**
 * 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
593
 * a single MSI irq upon its software driver call to request for
L
Linus Torvalds 已提交
594 595
 * MSI mode enabled on its hardware device function. A return of zero
 * indicates the successful setup of an entry zero with the new MSI
596
 * irq or non-zero for otherwise.
L
Linus Torvalds 已提交
597 598 599
 **/
int pci_enable_msi(struct pci_dev* dev)
{
E
Eric W. Biederman 已提交
600
	int pos, status;
L
Linus Torvalds 已提交
601

602 603
	if (pci_msi_supported(dev) < 0)
		return -EINVAL;
604

605 606
	status = msi_init();
	if (status < 0)
L
Linus Torvalds 已提交
607 608
		return status;

609 610
	pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
	if (!pos)
L
Linus Torvalds 已提交
611 612
		return -EINVAL;

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

615
	/* Check whether driver already requested for MSI-X irqs */
616 617 618 619 620
	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 已提交
621 622 623 624 625 626 627 628
	}
	status = msi_capability_init(dev);
	return status;
}

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

631 632
	if (!pci_msi_enable)
		return;
633 634
	if (!dev)
		return;
635

E
Eric W. Biederman 已提交
636 637 638
	if (!dev->msi_enabled)
		return;

639 640 641
	msi_set_enable(dev, 0);
	pci_intx(dev, 1);		/* enable intx */
	dev->msi_enabled = 0;
642

643
	entry = get_irq_msi(dev->first_msi_irq);
L
Linus Torvalds 已提交
644 645 646
	if (!entry || !entry->dev || entry->msi_attrib.type != PCI_CAP_ID_MSI) {
		return;
	}
E
Eric W. Biederman 已提交
647
	if (irq_has_action(dev->first_msi_irq)) {
L
Linus Torvalds 已提交
648
		printk(KERN_WARNING "PCI: %s: pci_disable_msi() called without "
649
		       "free_irq() on MSI irq %d\n",
E
Eric W. Biederman 已提交
650 651
		       pci_name(dev), dev->first_msi_irq);
		BUG_ON(irq_has_action(dev->first_msi_irq));
L
Linus Torvalds 已提交
652
	} else {
653
		default_irq = entry->msi_attrib.default_irq;
E
Eric W. Biederman 已提交
654
		msi_free_irq(dev, dev->first_msi_irq);
655

656 657
		/* Restore dev->irq to its default pin-assertion irq */
		dev->irq = default_irq;
L
Linus Torvalds 已提交
658
	}
E
Eric W. Biederman 已提交
659
	dev->first_msi_irq = 0;
L
Linus Torvalds 已提交
660 661
}

662
static int msi_free_irq(struct pci_dev* dev, int irq)
L
Linus Torvalds 已提交
663 664 665 666 667
{
	struct msi_desc *entry;
	int head, entry_nr, type;
	void __iomem *base;

668
	entry = get_irq_msi(irq);
L
Linus Torvalds 已提交
669 670 671 672 673 674 675
	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;
676 677
	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 已提交
678

679 680
	arch_teardown_msi_irq(irq);
	kmem_cache_free(msi_cachep, entry);
L
Linus Torvalds 已提交
681 682

	if (type == PCI_CAP_ID_MSIX) {
683 684
		writel(1, base + entry_nr * PCI_MSIX_ENTRY_SIZE +
			PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
L
Linus Torvalds 已提交
685

686
		if (head == irq)
L
Linus Torvalds 已提交
687 688 689 690 691 692 693 694 695
			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
696
 * @entries: pointer to an array of MSI-X entries
697
 * @nvec: number of MSI-X irqs requested for allocation by device driver
L
Linus Torvalds 已提交
698 699
 *
 * Setup the MSI-X capability structure of device function with the number
700
 * of requested irqs upon its software driver call to request for
L
Linus Torvalds 已提交
701 702
 * MSI-X mode enabled on its hardware device function. A return of zero
 * indicates the successful configuration of MSI-X capability structure
703
 * with new allocated MSI-X irqs. A return of < 0 indicates a failure.
L
Linus Torvalds 已提交
704
 * Or a return of > 0 indicates that driver request is exceeding the number
705
 * of irqs available. Driver should use the returned value to re-send
L
Linus Torvalds 已提交
706 707 708 709
 * its request.
 **/
int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec)
{
710
	int status, pos, nr_entries;
E
Eric W. Biederman 已提交
711
	int i, j;
L
Linus Torvalds 已提交
712 713
	u16 control;

714
	if (!entries || pci_msi_supported(dev) < 0)
L
Linus Torvalds 已提交
715 716
 		return -EINVAL;

717 718
	status = msi_init();
	if (status < 0)
L
Linus Torvalds 已提交
719 720
		return status;

721 722
	pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
	if (!pos)
L
Linus Torvalds 已提交
723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738
 		return -EINVAL;

	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 已提交
739
	WARN_ON(!!dev->msix_enabled);
740

741
	/* Check whether driver already requested for MSI irq */
742
   	if (dev->msi_enabled) {
L
Linus Torvalds 已提交
743
		printk(KERN_INFO "PCI: %s: Can't enable MSI-X.  "
744
		       "Device already has an MSI irq assigned\n",
L
Linus Torvalds 已提交
745 746 747 748 749 750 751 752 753
		       pci_name(dev));
		return -EINVAL;
	}
	status = msix_capability_init(dev, entries, nvec);
	return status;
}

void pci_disable_msix(struct pci_dev* dev)
{
E
Eric W. Biederman 已提交
754
	int irq, head, tail = 0, warning = 0;
L
Linus Torvalds 已提交
755

756 757
	if (!pci_msi_enable)
		return;
758 759 760
	if (!dev)
		return;

E
Eric W. Biederman 已提交
761 762 763
	if (!dev->msix_enabled)
		return;

764 765 766
	msix_set_enable(dev, 0);
	pci_intx(dev, 1);		/* enable intx */
	dev->msix_enabled = 0;
767

E
Eric W. Biederman 已提交
768 769
	irq = head = dev->first_msi_irq;
	while (head != tail) {
770
		tail = get_irq_msi(irq)->link.tail;
E
Eric W. Biederman 已提交
771 772 773 774 775 776 777 778 779 780 781 782
		if (irq_has_action(irq))
			warning = 1;
		else if (irq != head)	/* Release MSI-X irq */
			msi_free_irq(dev, irq);
		irq = tail;
	}
	msi_free_irq(dev, irq);
	if (warning) {
		printk(KERN_WARNING "PCI: %s: pci_disable_msix() called without "
			"free_irq() on all MSI-X irqs\n",
			pci_name(dev));
		BUG_ON(warning > 0);
L
Linus Torvalds 已提交
783
	}
E
Eric W. Biederman 已提交
784
	dev->first_msi_irq = 0;
L
Linus Torvalds 已提交
785 786 787
}

/**
788
 * msi_remove_pci_irq_vectors - reclaim MSI(X) irqs to unused state
L
Linus Torvalds 已提交
789 790
 * @dev: pointer to the pci_dev data structure of MSI(X) device function
 *
791
 * Being called during hotplug remove, from which the device function
792
 * is hot-removed. All previous assigned MSI/MSI-X irqs, if
L
Linus Torvalds 已提交
793 794 795 796 797 798 799 800
 * 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;

801
	if (dev->msi_enabled) {
E
Eric W. Biederman 已提交
802
		if (irq_has_action(dev->first_msi_irq)) {
L
Linus Torvalds 已提交
803
			printk(KERN_WARNING "PCI: %s: msi_remove_pci_irq_vectors() "
804
			       "called without free_irq() on MSI irq %d\n",
E
Eric W. Biederman 已提交
805 806
			       pci_name(dev), dev->first_msi_irq);
			BUG_ON(irq_has_action(dev->first_msi_irq));
807
		} else /* Release MSI irq assigned to this device */
E
Eric W. Biederman 已提交
808
			msi_free_irq(dev, dev->first_msi_irq);
L
Linus Torvalds 已提交
809
	}
810
	if (dev->msix_enabled) {
811
		int irq, head, tail = 0, warning = 0;
L
Linus Torvalds 已提交
812 813
		void __iomem *base = NULL;

E
Eric W. Biederman 已提交
814
		irq = head = dev->first_msi_irq;
L
Linus Torvalds 已提交
815
		while (head != tail) {
816 817
			tail = get_irq_msi(irq)->link.tail;
			base = get_irq_msi(irq)->mask_base;
818
			if (irq_has_action(irq))
L
Linus Torvalds 已提交
819
				warning = 1;
820 821 822
			else if (irq != head) /* Release MSI-X irq */
				msi_free_irq(dev, irq);
			irq = tail;
L
Linus Torvalds 已提交
823
		}
824
		msi_free_irq(dev, irq);
L
Linus Torvalds 已提交
825 826 827
		if (warning) {
			iounmap(base);
			printk(KERN_WARNING "PCI: %s: msi_remove_pci_irq_vectors() "
828
			       "called without free_irq() on all MSI-X irqs\n",
L
Linus Torvalds 已提交
829 830 831 832 833 834
			       pci_name(dev));
			BUG_ON(warning > 0);
		}
	}
}

835 836 837 838 839
void pci_no_msi(void)
{
	pci_msi_enable = 0;
}

L
Linus Torvalds 已提交
840 841 842 843
EXPORT_SYMBOL(pci_enable_msi);
EXPORT_SYMBOL(pci_disable_msi);
EXPORT_SYMBOL(pci_enable_msix);
EXPORT_SYMBOL(pci_disable_msix);