msi.c 22.9 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 DEFINE_SPINLOCK(msi_lock);
static struct msi_desc* msi_desc[NR_IRQS] = { [0 ... NR_IRQS-1] = NULL };
29
static struct kmem_cache* msi_cachep;
L
Linus Torvalds 已提交
30 31 32 33 34

static int pci_msi_enable = 1;

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

	return 0;
}

43
static void msi_set_mask_bit(unsigned int irq, int flag)
L
Linus Torvalds 已提交
44 45 46
{
	struct msi_desc *entry;

47
	entry = msi_desc[irq];
48
	BUG_ON(!entry || !entry->dev);
L
Linus Torvalds 已提交
49 50
	switch (entry->msi_attrib.type) {
	case PCI_CAP_ID_MSI:
51
		if (entry->msi_attrib.maskbit) {
S
Satoru Takeuchi 已提交
52 53
			int pos;
			u32 mask_bits;
54 55 56 57 58 59 60

			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 已提交
61 62 63 64 65 66 67 68 69
		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:
70
		BUG();
L
Linus Torvalds 已提交
71 72 73 74
		break;
	}
}

75
void read_msi_msg(unsigned int irq, struct msi_msg *msg)
L
Linus Torvalds 已提交
76
{
77
	struct msi_desc *entry = get_irq_data(irq);
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
	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 已提交
113

114
void write_msi_msg(unsigned int irq, struct msi_msg *msg)
115
{
116
	struct msi_desc *entry = get_irq_data(irq);
L
Linus Torvalds 已提交
117 118 119
	switch (entry->msi_attrib.type) {
	case PCI_CAP_ID_MSI:
	{
120 121 122 123 124 125 126 127 128 129 130 131 132 133
		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 已提交
134 135 136 137
		break;
	}
	case PCI_CAP_ID_MSIX:
	{
138 139 140 141 142 143 144 145 146
		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 已提交
147 148 149
		break;
	}
	default:
150
		BUG();
L
Linus Torvalds 已提交
151 152
	}
}
153

154
void mask_msi_irq(unsigned int irq)
L
Linus Torvalds 已提交
155
{
156
	msi_set_mask_bit(irq, 1);
L
Linus Torvalds 已提交
157 158
}

159
void unmask_msi_irq(unsigned int irq)
L
Linus Torvalds 已提交
160
{
161
	msi_set_mask_bit(irq, 0);
L
Linus Torvalds 已提交
162 163
}

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

L
Linus Torvalds 已提交
166 167 168 169 170 171 172
static int msi_init(void)
{
	static int status = -ENOMEM;

	if (!status)
		return status;

173 174
	status = msi_cache_init();
	if (status < 0) {
L
Linus Torvalds 已提交
175 176 177 178
		pci_msi_enable = 0;
		printk(KERN_WARNING "PCI: MSI cache init failed\n");
		return status;
	}
179

L
Linus Torvalds 已提交
180 181 182 183 184 185 186
	return status;
}

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

187
	entry = kmem_cache_zalloc(msi_cachep, GFP_KERNEL);
L
Linus Torvalds 已提交
188 189 190 191 192 193 194 195 196
	if (!entry)
		return NULL;

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

	return entry;
}

197
static void attach_msi_entry(struct msi_desc *entry, int irq)
L
Linus Torvalds 已提交
198 199 200 201
{
	unsigned long flags;

	spin_lock_irqsave(&msi_lock, flags);
202
	msi_desc[irq] = entry;
L
Linus Torvalds 已提交
203 204 205
	spin_unlock_irqrestore(&msi_lock, flags);
}

206
static int create_msi_irq(void)
L
Linus Torvalds 已提交
207
{
208 209 210 211 212 213
	struct msi_desc *entry;
	int irq;

	entry = alloc_msi_entry();
	if (!entry)
		return -ENOMEM;
214

215 216 217 218
	irq = create_irq();
	if (irq < 0) {
		kmem_cache_free(msi_cachep, entry);
		return -EBUSY;
L
Linus Torvalds 已提交
219
	}
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234

	set_irq_data(irq, entry);

	return irq;
}

static void destroy_msi_irq(unsigned int irq)
{
	struct msi_desc *entry;

	entry = get_irq_data(irq);
	set_irq_chip(irq, NULL);
	set_irq_data(irq, NULL);
	destroy_irq(irq);
	kmem_cache_free(msi_cachep, entry);
L
Linus Torvalds 已提交
235 236 237 238 239 240 241 242 243 244 245
}

static void enable_msi_mode(struct pci_dev *dev, int pos, int type)
{
	u16 control;

	pci_read_config_word(dev, msi_control_reg(pos), &control);
	if (type == PCI_CAP_ID_MSI) {
		/* Set enabled bits to single MSI & enable MSI_enable bit */
		msi_enable(control, 1);
		pci_write_config_word(dev, msi_control_reg(pos), control);
246
		dev->msi_enabled = 1;
L
Linus Torvalds 已提交
247 248 249
	} else {
		msix_enable(control);
		pci_write_config_word(dev, msi_control_reg(pos), control);
250
		dev->msix_enabled = 1;
L
Linus Torvalds 已提交
251
	}
252 253

	pci_intx(dev, 0);  /* disable intx */
L
Linus Torvalds 已提交
254 255
}

256
void disable_msi_mode(struct pci_dev *dev, int pos, int type)
L
Linus Torvalds 已提交
257 258 259 260 261 262 263 264
{
	u16 control;

	pci_read_config_word(dev, msi_control_reg(pos), &control);
	if (type == PCI_CAP_ID_MSI) {
		/* Set enabled bits to single MSI & enable MSI_enable bit */
		msi_disable(control);
		pci_write_config_word(dev, msi_control_reg(pos), control);
265
		dev->msi_enabled = 0;
L
Linus Torvalds 已提交
266 267 268
	} else {
		msix_disable(control);
		pci_write_config_word(dev, msi_control_reg(pos), control);
269
		dev->msix_enabled = 0;
L
Linus Torvalds 已提交
270
	}
271 272

	pci_intx(dev, 1);  /* enable intx */
L
Linus Torvalds 已提交
273 274
}

275
#ifdef CONFIG_PM
276
static int __pci_save_msi_state(struct pci_dev *dev)
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
{
	int pos, i = 0;
	u16 control;
	struct pci_cap_saved_state *save_state;
	u32 *cap;

	pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
	if (pos <= 0 || dev->no_msi)
		return 0;

	pci_read_config_word(dev, msi_control_reg(pos), &control);
	if (!(control & PCI_MSI_FLAGS_ENABLE))
		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;
}

314
static void __pci_restore_msi_state(struct pci_dev *dev)
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
{
	int i = 0, pos;
	u16 control;
	struct pci_cap_saved_state *save_state;
	u32 *cap;

	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];

	control = cap[i++] >> 16;
	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);
	enable_msi_mode(dev, pos, PCI_CAP_ID_MSI);
	pci_remove_saved_cap(save_state);
	kfree(save_state);
}

342
static int __pci_save_msix_state(struct pci_dev *dev)
343 344
{
	int pos;
345
	int irq, head, tail = 0;
346 347 348
	u16 control;
	struct pci_cap_saved_state *save_state;

E
Eric W. Biederman 已提交
349 350 351
	if (!dev->msix_enabled)
		return 0;

352 353 354 355
	pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
	if (pos <= 0 || dev->no_msi)
		return 0;

356
	/* save the capability */
357 358 359 360 361 362 363 364 365 366 367
	pci_read_config_word(dev, msi_control_reg(pos), &control);
	if (!(control & PCI_MSIX_FLAGS_ENABLE))
		return 0;
	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;

368
	/* save the table */
E
Eric W. Biederman 已提交
369
	irq = head = dev->first_msi_irq;
370 371 372
	while (head != tail) {
		struct msi_desc *entry;

373
		entry = msi_desc[irq];
374
		read_msi_msg(irq, &entry->msg_save);
375

376 377
		tail = msi_desc[irq]->link.tail;
		irq = tail;
378 379
	}

380 381 382 383 384
	save_state->cap_nr = PCI_CAP_ID_MSIX;
	pci_add_saved_cap(dev, save_state);
	return 0;
}

385 386 387 388 389 390 391 392 393 394 395 396 397 398
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)
399 400 401
{
	u16 save;
	int pos;
402
	int irq, head, tail = 0;
403 404 405
	struct msi_desc *entry;
	struct pci_cap_saved_state *save_state;

E
Eric W. Biederman 已提交
406 407 408
	if (!dev->msix_enabled)
		return;

409 410 411 412 413 414 415 416 417 418 419 420
	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 */
E
Eric W. Biederman 已提交
421
	irq = head = dev->first_msi_irq;
422
	while (head != tail) {
423
		entry = msi_desc[irq];
424
		write_msi_msg(irq, &entry->msg_save);
425

426 427
		tail = msi_desc[irq]->link.tail;
		irq = tail;
428 429 430 431 432
	}

	pci_write_config_word(dev, msi_control_reg(pos), save);
	enable_msi_mode(dev, pos, PCI_CAP_ID_MSIX);
}
433 434 435 436 437 438

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

L
Linus Torvalds 已提交
441 442 443 444
/**
 * msi_capability_init - configure device's MSI capability structure
 * @dev: pointer to the pci_dev data structure of MSI device function
 *
445
 * Setup the MSI capability structure of device function with a single
446
 * MSI irq, regardless of device function is capable of handling
L
Linus Torvalds 已提交
447
 * multiple messages. A return of zero indicates the successful setup
448
 * of an entry zero with the new MSI irq or non-zero for otherwise.
L
Linus Torvalds 已提交
449 450 451
 **/
static int msi_capability_init(struct pci_dev *dev)
{
452
	int status;
L
Linus Torvalds 已提交
453
	struct msi_desc *entry;
454
	int pos, irq;
L
Linus Torvalds 已提交
455 456 457 458 459
	u16 control;

   	pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
	pci_read_config_word(dev, msi_control_reg(pos), &control);
	/* MSI Entry Initialization */
460
	irq = create_msi_irq();
461 462 463 464 465 466
	if (irq < 0)
		return irq;

	entry = get_irq_data(irq);
	entry->link.head = irq;
	entry->link.tail = irq;
L
Linus Torvalds 已提交
467
	entry->msi_attrib.type = PCI_CAP_ID_MSI;
468
	entry->msi_attrib.is_64 = is_64bit_address(control);
L
Linus Torvalds 已提交
469 470
	entry->msi_attrib.entry_nr = 0;
	entry->msi_attrib.maskbit = is_mask_bit_support(control);
471
	entry->msi_attrib.default_irq = dev->irq;	/* Save IOAPIC IRQ */
472
	entry->msi_attrib.pos = pos;
L
Linus Torvalds 已提交
473 474 475 476
	if (is_mask_bit_support(control)) {
		entry->mask_base = (void __iomem *)(long)msi_mask_bits_reg(pos,
				is_64bit_address(control));
	}
477 478 479 480 481 482 483 484 485 486 487 488 489 490
	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 已提交
491
	/* Configure MSI capability structure */
492 493
	status = arch_setup_msi_irq(irq, dev);
	if (status < 0) {
494
		destroy_msi_irq(irq);
495 496
		return status;
	}
497

E
Eric W. Biederman 已提交
498
	dev->first_msi_irq = irq;
499
	attach_msi_entry(entry, irq);
L
Linus Torvalds 已提交
500 501 502
	/* Set MSI enabled bits	 */
	enable_msi_mode(dev, pos, PCI_CAP_ID_MSI);

503
	dev->irq = irq;
L
Linus Torvalds 已提交
504 505 506 507 508 509
	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 已提交
510 511
 * @entries: pointer to an array of struct msix_entry entries
 * @nvec: number of @entries
L
Linus Torvalds 已提交
512
 *
513
 * Setup the MSI-X capability structure of device function with a
514 515
 * 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 已提交
516 517 518 519 520
 **/
static int msix_capability_init(struct pci_dev *dev,
				struct msix_entry *entries, int nvec)
{
	struct msi_desc *head = NULL, *tail = NULL, *entry = NULL;
521
	int status;
522
	int irq, pos, i, j, nr_entries, temp = 0;
523 524
	unsigned long phys_addr;
	u32 table_offset;
L
Linus Torvalds 已提交
525 526 527 528 529 530 531 532
 	u16 control;
	u8 bir;
	void __iomem *base;

   	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);
533 534

 	pci_read_config_dword(dev, msix_table_offset_reg(pos), &table_offset);
L
Linus Torvalds 已提交
535
	bir = (u8)(table_offset & PCI_MSIX_FLAGS_BIRMASK);
536 537
	table_offset &= ~PCI_MSIX_FLAGS_BIRMASK;
	phys_addr = pci_resource_start (dev, bir) + table_offset;
L
Linus Torvalds 已提交
538 539 540 541 542 543
	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++) {
544
		irq = create_msi_irq();
545
		if (irq < 0)
L
Linus Torvalds 已提交
546 547
			break;

548
		entry = get_irq_data(irq);
L
Linus Torvalds 已提交
549
 		j = entries[i].entry;
550
 		entries[i].vector = irq;
L
Linus Torvalds 已提交
551
		entry->msi_attrib.type = PCI_CAP_ID_MSIX;
552
		entry->msi_attrib.is_64 = 1;
L
Linus Torvalds 已提交
553 554
		entry->msi_attrib.entry_nr = j;
		entry->msi_attrib.maskbit = 1;
555
		entry->msi_attrib.default_irq = dev->irq;
556
		entry->msi_attrib.pos = pos;
L
Linus Torvalds 已提交
557 558 559
		entry->dev = dev;
		entry->mask_base = base;
		if (!head) {
560 561
			entry->link.head = irq;
			entry->link.tail = irq;
L
Linus Torvalds 已提交
562 563 564 565
			head = entry;
		} else {
			entry->link.head = temp;
			entry->link.tail = tail->link.tail;
566 567
			tail->link.tail = irq;
			head->link.head = irq;
L
Linus Torvalds 已提交
568
		}
569
		temp = irq;
L
Linus Torvalds 已提交
570 571
		tail = entry;
		/* Configure MSI-X capability structure */
572
		status = arch_setup_msi_irq(irq, dev);
573 574
		if (status < 0) {
			destroy_msi_irq(irq);
575
			break;
576
		}
577

578
		attach_msi_entry(entry, irq);
L
Linus Torvalds 已提交
579 580
	}
	if (i != nvec) {
581
		int avail = i - 1;
L
Linus Torvalds 已提交
582 583
		i--;
		for (; i >= 0; i--) {
584 585
			irq = (entries + i)->vector;
			msi_free_irq(dev, irq);
L
Linus Torvalds 已提交
586 587
			(entries + i)->vector = 0;
		}
588 589 590 591 592 593
		/* 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 已提交
594
	}
E
Eric W. Biederman 已提交
595
	dev->first_msi_irq = entries[0].vector;
L
Linus Torvalds 已提交
596 597 598 599 600 601
	/* Set MSI-X enabled bits */
	enable_msi_mode(dev, pos, PCI_CAP_ID_MSIX);

	return 0;
}

602 603 604 605
/**
 * pci_msi_supported - check whether MSI may be enabled on device
 * @dev: pointer to the pci_dev data structure of MSI device function
 *
606 607
 * Look at global flags, the device itself, and its parent busses
 * to return 0 if MSI are supported for the device.
608 609 610 611 612 613
 **/
static
int pci_msi_supported(struct pci_dev * dev)
{
	struct pci_bus *bus;

614
	/* MSI must be globally enabled and supported by the device */
615 616 617
	if (!pci_msi_enable || !dev || dev->no_msi)
		return -EINVAL;

618 619 620 621 622 623
	/* 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.
	 */
624 625 626 627 628 629 630
	for (bus = dev->bus; bus; bus = bus->parent)
		if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI)
			return -EINVAL;

	return 0;
}

L
Linus Torvalds 已提交
631 632 633 634 635
/**
 * 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
636
 * a single MSI irq upon its software driver call to request for
L
Linus Torvalds 已提交
637 638
 * MSI mode enabled on its hardware device function. A return of zero
 * indicates the successful setup of an entry zero with the new MSI
639
 * irq or non-zero for otherwise.
L
Linus Torvalds 已提交
640 641 642
 **/
int pci_enable_msi(struct pci_dev* dev)
{
E
Eric W. Biederman 已提交
643
	int pos, status;
L
Linus Torvalds 已提交
644

645 646
	if (pci_msi_supported(dev) < 0)
		return -EINVAL;
647

648 649
	status = msi_init();
	if (status < 0)
L
Linus Torvalds 已提交
650 651
		return status;

652 653
	pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
	if (!pos)
L
Linus Torvalds 已提交
654 655
		return -EINVAL;

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

658
	/* Check whether driver already requested for MSI-X irqs */
659
	pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
E
Eric W. Biederman 已提交
660
	if (pos > 0 && dev->msix_enabled) {
L
Linus Torvalds 已提交
661
			printk(KERN_INFO "PCI: %s: Can't enable MSI.  "
E
Eric W. Biederman 已提交
662
			       "Device already has MSI-X enabled\n",
L
Linus Torvalds 已提交
663 664 665 666 667 668 669 670 671 672
			       pci_name(dev));
			return -EINVAL;
	}
	status = msi_capability_init(dev);
	return status;
}

void pci_disable_msi(struct pci_dev* dev)
{
	struct msi_desc *entry;
673
	int pos, default_irq;
L
Linus Torvalds 已提交
674 675 676
	u16 control;
	unsigned long flags;

677 678
	if (!pci_msi_enable)
		return;
679 680
	if (!dev)
		return;
681

E
Eric W. Biederman 已提交
682 683 684
	if (!dev->msi_enabled)
		return;

685 686
	pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
	if (!pos)
L
Linus Torvalds 已提交
687 688 689 690 691 692
		return;

	pci_read_config_word(dev, msi_control_reg(pos), &control);
	if (!(control & PCI_MSI_FLAGS_ENABLE))
		return;

E
Eric W. Biederman 已提交
693

694 695
	disable_msi_mode(dev, pos, PCI_CAP_ID_MSI);

L
Linus Torvalds 已提交
696
	spin_lock_irqsave(&msi_lock, flags);
E
Eric W. Biederman 已提交
697
	entry = msi_desc[dev->first_msi_irq];
L
Linus Torvalds 已提交
698 699 700 701
	if (!entry || !entry->dev || entry->msi_attrib.type != PCI_CAP_ID_MSI) {
		spin_unlock_irqrestore(&msi_lock, flags);
		return;
	}
E
Eric W. Biederman 已提交
702
	if (irq_has_action(dev->first_msi_irq)) {
L
Linus Torvalds 已提交
703 704
		spin_unlock_irqrestore(&msi_lock, flags);
		printk(KERN_WARNING "PCI: %s: pci_disable_msi() called without "
705
		       "free_irq() on MSI irq %d\n",
E
Eric W. Biederman 已提交
706 707
		       pci_name(dev), dev->first_msi_irq);
		BUG_ON(irq_has_action(dev->first_msi_irq));
L
Linus Torvalds 已提交
708
	} else {
709
		default_irq = entry->msi_attrib.default_irq;
L
Linus Torvalds 已提交
710
		spin_unlock_irqrestore(&msi_lock, flags);
E
Eric W. Biederman 已提交
711
		msi_free_irq(dev, dev->first_msi_irq);
712

713 714
		/* Restore dev->irq to its default pin-assertion irq */
		dev->irq = default_irq;
L
Linus Torvalds 已提交
715
	}
E
Eric W. Biederman 已提交
716
	dev->first_msi_irq = 0;
L
Linus Torvalds 已提交
717 718
}

719
static int msi_free_irq(struct pci_dev* dev, int irq)
L
Linus Torvalds 已提交
720 721 722 723 724 725
{
	struct msi_desc *entry;
	int head, entry_nr, type;
	void __iomem *base;
	unsigned long flags;

726
	arch_teardown_msi_irq(irq);
727

L
Linus Torvalds 已提交
728
	spin_lock_irqsave(&msi_lock, flags);
729
	entry = msi_desc[irq];
L
Linus Torvalds 已提交
730 731 732 733 734 735 736 737 738 739 740
	if (!entry || entry->dev != dev) {
		spin_unlock_irqrestore(&msi_lock, flags);
		return -EINVAL;
	}
	type = entry->msi_attrib.type;
	entry_nr = entry->msi_attrib.entry_nr;
	head = entry->link.head;
	base = entry->mask_base;
	msi_desc[entry->link.head]->link.tail = entry->link.tail;
	msi_desc[entry->link.tail]->link.head = entry->link.head;
	entry->dev = NULL;
741
	msi_desc[irq] = NULL;
L
Linus Torvalds 已提交
742 743
	spin_unlock_irqrestore(&msi_lock, flags);

744
	destroy_msi_irq(irq);
L
Linus Torvalds 已提交
745 746

	if (type == PCI_CAP_ID_MSIX) {
747 748
		writel(1, base + entry_nr * PCI_MSIX_ENTRY_SIZE +
			PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
L
Linus Torvalds 已提交
749

750
		if (head == irq)
L
Linus Torvalds 已提交
751 752 753 754 755 756 757 758 759
			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
760
 * @entries: pointer to an array of MSI-X entries
761
 * @nvec: number of MSI-X irqs requested for allocation by device driver
L
Linus Torvalds 已提交
762 763
 *
 * Setup the MSI-X capability structure of device function with the number
764
 * of requested irqs upon its software driver call to request for
L
Linus Torvalds 已提交
765 766
 * MSI-X mode enabled on its hardware device function. A return of zero
 * indicates the successful configuration of MSI-X capability structure
767
 * with new allocated MSI-X irqs. A return of < 0 indicates a failure.
L
Linus Torvalds 已提交
768
 * Or a return of > 0 indicates that driver request is exceeding the number
769
 * of irqs available. Driver should use the returned value to re-send
L
Linus Torvalds 已提交
770 771 772 773
 * its request.
 **/
int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec)
{
774
	int status, pos, nr_entries;
E
Eric W. Biederman 已提交
775
	int i, j;
L
Linus Torvalds 已提交
776 777
	u16 control;

778
	if (!entries || pci_msi_supported(dev) < 0)
L
Linus Torvalds 已提交
779 780
 		return -EINVAL;

781 782
	status = msi_init();
	if (status < 0)
L
Linus Torvalds 已提交
783 784
		return status;

785 786
	pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
	if (!pos)
L
Linus Torvalds 已提交
787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802
 		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 已提交
803
	WARN_ON(!!dev->msix_enabled);
804

805
	/* Check whether driver already requested for MSI irq */
L
Linus Torvalds 已提交
806
   	if (pci_find_capability(dev, PCI_CAP_ID_MSI) > 0 &&
E
Eric W. Biederman 已提交
807
		dev->msi_enabled) {
L
Linus Torvalds 已提交
808
		printk(KERN_INFO "PCI: %s: Can't enable MSI-X.  "
809
		       "Device already has an MSI irq assigned\n",
L
Linus Torvalds 已提交
810 811 812 813 814 815 816 817 818
		       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 已提交
819 820 821
	int irq, head, tail = 0, warning = 0;
	unsigned long flags;
	int pos;
L
Linus Torvalds 已提交
822 823
	u16 control;

824 825
	if (!pci_msi_enable)
		return;
826 827 828
	if (!dev)
		return;

E
Eric W. Biederman 已提交
829 830 831
	if (!dev->msix_enabled)
		return;

832 833
	pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
	if (!pos)
L
Linus Torvalds 已提交
834 835 836 837 838 839
		return;

	pci_read_config_word(dev, msi_control_reg(pos), &control);
	if (!(control & PCI_MSIX_FLAGS_ENABLE))
		return;

840 841
	disable_msi_mode(dev, pos, PCI_CAP_ID_MSIX);

E
Eric W. Biederman 已提交
842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858
	irq = head = dev->first_msi_irq;
	while (head != tail) {
		spin_lock_irqsave(&msi_lock, flags);
		tail = msi_desc[irq]->link.tail;
		spin_unlock_irqrestore(&msi_lock, flags);
		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 已提交
859
	}
E
Eric W. Biederman 已提交
860
	dev->first_msi_irq = 0;
L
Linus Torvalds 已提交
861 862 863
}

/**
864
 * msi_remove_pci_irq_vectors - reclaim MSI(X) irqs to unused state
L
Linus Torvalds 已提交
865 866
 * @dev: pointer to the pci_dev data structure of MSI(X) device function
 *
867
 * Being called during hotplug remove, from which the device function
868
 * is hot-removed. All previous assigned MSI/MSI-X irqs, if
L
Linus Torvalds 已提交
869 870 871 872 873
 * 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)
{
E
Eric W. Biederman 已提交
874
	int pos;
L
Linus Torvalds 已提交
875 876 877 878 879
	unsigned long flags;

	if (!pci_msi_enable || !dev)
 		return;

880
	pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
E
Eric W. Biederman 已提交
881 882
	if (pos > 0 && dev->msi_enabled) {
		if (irq_has_action(dev->first_msi_irq)) {
L
Linus Torvalds 已提交
883
			printk(KERN_WARNING "PCI: %s: msi_remove_pci_irq_vectors() "
884
			       "called without free_irq() on MSI irq %d\n",
E
Eric W. Biederman 已提交
885 886
			       pci_name(dev), dev->first_msi_irq);
			BUG_ON(irq_has_action(dev->first_msi_irq));
887
		} else /* Release MSI irq assigned to this device */
E
Eric W. Biederman 已提交
888
			msi_free_irq(dev, dev->first_msi_irq);
L
Linus Torvalds 已提交
889
	}
890
	pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
E
Eric W. Biederman 已提交
891
	if (pos > 0 && dev->msix_enabled) {
892
		int irq, head, tail = 0, warning = 0;
L
Linus Torvalds 已提交
893 894
		void __iomem *base = NULL;

E
Eric W. Biederman 已提交
895
		irq = head = dev->first_msi_irq;
L
Linus Torvalds 已提交
896 897
		while (head != tail) {
			spin_lock_irqsave(&msi_lock, flags);
898 899
			tail = msi_desc[irq]->link.tail;
			base = msi_desc[irq]->mask_base;
L
Linus Torvalds 已提交
900
			spin_unlock_irqrestore(&msi_lock, flags);
901
			if (irq_has_action(irq))
L
Linus Torvalds 已提交
902
				warning = 1;
903 904 905
			else if (irq != head) /* Release MSI-X irq */
				msi_free_irq(dev, irq);
			irq = tail;
L
Linus Torvalds 已提交
906
		}
907
		msi_free_irq(dev, irq);
L
Linus Torvalds 已提交
908 909 910
		if (warning) {
			iounmap(base);
			printk(KERN_WARNING "PCI: %s: msi_remove_pci_irq_vectors() "
911
			       "called without free_irq() on all MSI-X irqs\n",
L
Linus Torvalds 已提交
912 913 914 915 916 917
			       pci_name(dev));
			BUG_ON(warning > 0);
		}
	}
}

918 919 920 921 922
void pci_no_msi(void)
{
	pci_msi_enable = 0;
}

L
Linus Torvalds 已提交
923 924 925 926
EXPORT_SYMBOL(pci_enable_msi);
EXPORT_SYMBOL(pci_disable_msi);
EXPORT_SYMBOL(pci_enable_msix);
EXPORT_SYMBOL(pci_disable_msix);