msi.c 20.3 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
#include <linux/mm.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/pci.h>
#include <linux/proc_fs.h>
17
#include <linux/msi.h>
D
Dan Williams 已提交
18
#include <linux/smp.h>
L
Linus Torvalds 已提交
19 20 21 22 23 24 25 26 27

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

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

static int pci_msi_enable = 1;

28 29
/* Arch hooks */

30 31
#ifndef arch_msi_check_device
int arch_msi_check_device(struct pci_dev *dev, int nvec, int type)
32 33 34
{
	return 0;
}
35
#endif
36

37 38
#ifndef arch_setup_msi_irqs
int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
39 40 41 42
{
	struct msi_desc *entry;
	int ret;

43 44 45 46 47 48 49
	/*
	 * If an architecture wants to support multiple MSI, it needs to
	 * override arch_setup_msi_irqs()
	 */
	if (type == PCI_CAP_ID_MSI && nvec > 1)
		return 1;

50 51
	list_for_each_entry(entry, &dev->msi_list, list) {
		ret = arch_setup_msi_irq(dev, entry);
52
		if (ret < 0)
53
			return ret;
54 55
		if (ret > 0)
			return -ENOSPC;
56 57 58 59
	}

	return 0;
}
60
#endif
61

62 63
#ifndef arch_teardown_msi_irqs
void arch_teardown_msi_irqs(struct pci_dev *dev)
64 65 66 67
{
	struct msi_desc *entry;

	list_for_each_entry(entry, &dev->msi_list, list) {
68 69 70 71 72 73
		int i, nvec;
		if (entry->irq == 0)
			continue;
		nvec = 1 << entry->msi_attrib.multiple;
		for (i = 0; i < nvec; i++)
			arch_teardown_msi_irq(entry->irq + i);
74 75
	}
}
76
#endif
77

78
static void __msi_set_enable(struct pci_dev *dev, int pos, int enable)
79 80 81 82 83 84 85 86 87 88 89 90
{
	u16 control;

	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);
	}
}

91 92 93 94 95
static void msi_set_enable(struct pci_dev *dev, int enable)
{
	__msi_set_enable(dev, pci_find_capability(dev, PCI_CAP_ID_MSI), enable);
}

96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
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);
	}
}

111 112
static inline __attribute_const__ u32 msi_mask(unsigned x)
{
113 114 115 116
	/* Don't shift by >= width of type */
	if (x >= 5)
		return 0xffffffff;
	return (1 << (1 << x)) - 1;
117 118
}

119
static inline __attribute_const__ u32 msi_capable_mask(u16 control)
M
Mitch Williams 已提交
120
{
121 122
	return msi_mask((control >> 1) & 7);
}
M
Mitch Williams 已提交
123

124 125 126
static inline __attribute_const__ u32 msi_enabled_mask(u16 control)
{
	return msi_mask((control >> 4) & 7);
M
Mitch Williams 已提交
127 128
}

129 130 131 132 133 134
/*
 * PCI 2.3 does not specify mask bits for each MSI interrupt.  Attempting to
 * mask all MSI interrupts by clearing the MSI enable bit does not work
 * reliably as devices without an INTx disable bit will then generate a
 * level IRQ which will never be cleared.
 */
135
static void msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag)
L
Linus Torvalds 已提交
136
{
137
	u32 mask_bits = desc->masked;
L
Linus Torvalds 已提交
138

139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
	if (!desc->msi_attrib.maskbit)
		return;

	mask_bits &= ~mask;
	mask_bits |= flag;
	pci_write_config_dword(desc->dev, desc->mask_pos, mask_bits);
	desc->masked = mask_bits;
}

/*
 * This internal function does not flush PCI writes to the device.
 * All users must ensure that they read from the device before either
 * assuming that the device state is up to date, or returning out of this
 * file.  This saves a few milliseconds when initialising devices with lots
 * of MSI-X interrupts.
 */
static void msix_mask_irq(struct msi_desc *desc, u32 flag)
{
	u32 mask_bits = desc->masked;
	unsigned offset = desc->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE +
					PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET;
	mask_bits &= ~1;
	mask_bits |= flag;
	writel(mask_bits, desc->mask_base + offset);
	desc->masked = mask_bits;
}
165

166 167 168
static void msi_set_mask_bit(unsigned irq, u32 flag)
{
	struct msi_desc *desc = get_irq_msi(irq);
169

170 171 172 173
	if (desc->msi_attrib.is_msix) {
		msix_mask_irq(desc, flag);
		readl(desc->mask_base);		/* Flush write to device */
	} else {
174 175
		unsigned offset = irq - desc->dev->irq;
		msi_mask_irq(desc, 1 << offset, flag << offset);
L
Linus Torvalds 已提交
176
	}
177 178 179 180 181 182 183 184 185 186
}

void mask_msi_irq(unsigned int irq)
{
	msi_set_mask_bit(irq, 1);
}

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

Y
Yinghai Lu 已提交
189
void read_msi_msg_desc(struct irq_desc *desc, struct msi_msg *msg)
L
Linus Torvalds 已提交
190
{
Y
Yinghai Lu 已提交
191
	struct msi_desc *entry = get_irq_desc_msi(desc);
192 193 194 195 196 197 198 199
	if (entry->msi_attrib.is_msix) {
		void __iomem *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);
	} else {
200 201 202 203 204 205 206 207 208 209 210 211
		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;
212
			pci_read_config_word(dev, msi_data_reg(pos, 0), &data);
213 214 215 216
		}
		msg->data = data;
	}
}
L
Linus Torvalds 已提交
217

Y
Yinghai Lu 已提交
218
void read_msi_msg(unsigned int irq, struct msi_msg *msg)
219
{
Y
Yinghai Lu 已提交
220 221 222 223 224 225 226 227
	struct irq_desc *desc = irq_to_desc(irq);

	read_msi_msg_desc(desc, msg);
}

void write_msi_msg_desc(struct irq_desc *desc, struct msi_msg *msg)
{
	struct msi_desc *entry = get_irq_desc_msi(desc);
228 229 230 231 232 233 234 235 236 237 238
	if (entry->msi_attrib.is_msix) {
		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);
	} else {
239 240
		struct pci_dev *dev = entry->dev;
		int pos = entry->msi_attrib.pos;
241 242 243 244 245 246
		u16 msgctl;

		pci_read_config_word(dev, msi_control_reg(pos), &msgctl);
		msgctl &= ~PCI_MSI_FLAGS_QSIZE;
		msgctl |= entry->msi_attrib.multiple << 4;
		pci_write_config_word(dev, msi_control_reg(pos), msgctl);
247 248 249 250 251 252 253 254 255 256 257 258

		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 已提交
259
	}
260
	entry->msg = *msg;
L
Linus Torvalds 已提交
261
}
262

Y
Yinghai Lu 已提交
263 264 265 266 267 268 269
void write_msi_msg(unsigned int irq, struct msi_msg *msg)
{
	struct irq_desc *desc = irq_to_desc(irq);

	write_msi_msg_desc(desc, msg);
}

270
static int msi_free_irqs(struct pci_dev* dev);
S
Satoru Takeuchi 已提交
271

272
static struct msi_desc *alloc_msi_entry(struct pci_dev *dev)
L
Linus Torvalds 已提交
273
{
274 275
	struct msi_desc *desc = kzalloc(sizeof(*desc), GFP_KERNEL);
	if (!desc)
L
Linus Torvalds 已提交
276 277
		return NULL;

278 279
	INIT_LIST_HEAD(&desc->list);
	desc->dev = dev;
L
Linus Torvalds 已提交
280

281
	return desc;
L
Linus Torvalds 已提交
282 283
}

284 285 286 287 288 289
static void pci_intx_for_msi(struct pci_dev *dev, int enable)
{
	if (!(dev->dev_flags & PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG))
		pci_intx(dev, enable);
}

290
static void __pci_restore_msi_state(struct pci_dev *dev)
291
{
292
	int pos;
293
	u16 control;
294
	struct msi_desc *entry;
295

296 297 298
	if (!dev->msi_enabled)
		return;

299 300
	entry = get_irq_msi(dev->irq);
	pos = entry->msi_attrib.pos;
301

302
	pci_intx_for_msi(dev, 0);
303
	msi_set_enable(dev, 0);
304 305 306
	write_msi_msg(dev->irq, &entry->msg);

	pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control);
307
	msi_mask_irq(entry, msi_capable_mask(control), entry->masked);
308
	control &= ~PCI_MSI_FLAGS_QSIZE;
309
	control |= (entry->msi_attrib.multiple << 4) | PCI_MSI_FLAGS_ENABLE;
310
	pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control);
311 312 313
}

static void __pci_restore_msix_state(struct pci_dev *dev)
314 315 316
{
	int pos;
	struct msi_desc *entry;
317
	u16 control;
318

E
Eric W. Biederman 已提交
319 320 321
	if (!dev->msix_enabled)
		return;

322
	/* route the table */
323
	pci_intx_for_msi(dev, 0);
324
	msix_set_enable(dev, 0);
325

326 327
	list_for_each_entry(entry, &dev->msi_list, list) {
		write_msi_msg(entry->irq, &entry->msg);
328
		msix_mask_irq(entry, entry->masked);
329 330
	}

331 332
	BUG_ON(list_empty(&dev->msi_list));
	entry = list_entry(dev->msi_list.next, struct msi_desc, list);
333
	pos = entry->msi_attrib.pos;
334 335 336 337
	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);
338
}
339 340 341 342 343 344

void pci_restore_msi_state(struct pci_dev *dev)
{
	__pci_restore_msi_state(dev);
	__pci_restore_msix_state(dev);
}
345
EXPORT_SYMBOL_GPL(pci_restore_msi_state);
346

L
Linus Torvalds 已提交
347 348 349
/**
 * msi_capability_init - configure device's MSI capability structure
 * @dev: pointer to the pci_dev data structure of MSI device function
350
 * @nvec: number of interrupts to allocate
L
Linus Torvalds 已提交
351
 *
352 353 354 355 356 357 358
 * Setup the MSI capability structure of the device with the requested
 * number of interrupts.  A return value of zero indicates the successful
 * setup of an entry with the new MSI irq.  A negative return value indicates
 * an error, and a positive return value indicates the number of interrupts
 * which could have been allocated.
 */
static int msi_capability_init(struct pci_dev *dev, int nvec)
L
Linus Torvalds 已提交
359 360
{
	struct msi_desc *entry;
361
	int pos, ret;
L
Linus Torvalds 已提交
362
	u16 control;
363
	unsigned mask;
L
Linus Torvalds 已提交
364

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

L
Linus Torvalds 已提交
367 368 369
   	pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
	pci_read_config_word(dev, msi_control_reg(pos), &control);
	/* MSI Entry Initialization */
370
	entry = alloc_msi_entry(dev);
371 372
	if (!entry)
		return -ENOMEM;
373

374
	entry->msi_attrib.is_msix = 0;
375
	entry->msi_attrib.is_64 = is_64bit_address(control);
L
Linus Torvalds 已提交
376 377
	entry->msi_attrib.entry_nr = 0;
	entry->msi_attrib.maskbit = is_mask_bit_support(control);
378
	entry->msi_attrib.default_irq = dev->irq;	/* Save IOAPIC IRQ */
379
	entry->msi_attrib.pos = pos;
380

381
	entry->mask_pos = msi_mask_reg(pos, entry->msi_attrib.is_64);
382 383 384 385 386 387
	/* All MSIs are unmasked by default, Mask them all */
	if (entry->msi_attrib.maskbit)
		pci_read_config_dword(dev, entry->mask_pos, &entry->masked);
	mask = msi_capable_mask(control);
	msi_mask_irq(entry, mask, mask);

388
	list_add_tail(&entry->list, &dev->msi_list);
389

L
Linus Torvalds 已提交
390
	/* Configure MSI capability structure */
391
	ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSI);
392
	if (ret) {
393
		msi_free_irqs(dev);
394
		return ret;
395
	}
396

L
Linus Torvalds 已提交
397
	/* Set MSI enabled bits	 */
398
	pci_intx_for_msi(dev, 0);
399 400
	msi_set_enable(dev, 1);
	dev->msi_enabled = 1;
L
Linus Torvalds 已提交
401

402
	dev->irq = entry->irq;
L
Linus Torvalds 已提交
403 404 405 406 407 408
	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 已提交
409 410
 * @entries: pointer to an array of struct msix_entry entries
 * @nvec: number of @entries
L
Linus Torvalds 已提交
411
 *
412
 * Setup the MSI-X capability structure of device function with a
413 414
 * 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 已提交
415 416 417 418
 **/
static int msix_capability_init(struct pci_dev *dev,
				struct msix_entry *entries, int nvec)
{
419
	struct msi_desc *entry;
420
	int pos, i, j, nr_entries, ret;
421 422
	unsigned long phys_addr;
	u32 table_offset;
L
Linus Torvalds 已提交
423 424 425 426
 	u16 control;
	u8 bir;
	void __iomem *base;

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

L
Linus Torvalds 已提交
429 430 431 432
   	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);
433 434

 	pci_read_config_dword(dev, msix_table_offset_reg(pos), &table_offset);
L
Linus Torvalds 已提交
435
	bir = (u8)(table_offset & PCI_MSIX_FLAGS_BIRMASK);
436 437
	table_offset &= ~PCI_MSIX_FLAGS_BIRMASK;
	phys_addr = pci_resource_start (dev, bir) + table_offset;
L
Linus Torvalds 已提交
438 439 440 441 442 443
	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++) {
444
		entry = alloc_msi_entry(dev);
445
		if (!entry)
L
Linus Torvalds 已提交
446 447 448
			break;

 		j = entries[i].entry;
449
		entry->msi_attrib.is_msix = 1;
450
		entry->msi_attrib.is_64 = 1;
L
Linus Torvalds 已提交
451
		entry->msi_attrib.entry_nr = j;
452
		entry->msi_attrib.default_irq = dev->irq;
453
		entry->msi_attrib.pos = pos;
L
Linus Torvalds 已提交
454
		entry->mask_base = base;
455
		msix_mask_irq(entry, 1);
456

457
		list_add_tail(&entry->list, &dev->msi_list);
L
Linus Torvalds 已提交
458
	}
459 460

	ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX);
461 462 463
	if (ret < 0) {
		/* If we had some success report the number of irqs
		 * we succeeded in setting up. */
464 465 466 467 468
		int avail = 0;
		list_for_each_entry(entry, &dev->msi_list, list) {
			if (entry->irq != 0) {
				avail++;
			}
L
Linus Torvalds 已提交
469
		}
470

471 472 473
		if (avail != 0)
			ret = avail;
	}
474

475 476 477
	if (ret) {
		msi_free_irqs(dev);
		return ret;
L
Linus Torvalds 已提交
478
	}
479 480 481 482 483 484 485

	i = 0;
	list_for_each_entry(entry, &dev->msi_list, list) {
		entries[i].vector = entry->irq;
		set_irq_msi(entry->irq, entry);
		i++;
	}
L
Linus Torvalds 已提交
486
	/* Set MSI-X enabled bits */
487
	pci_intx_for_msi(dev, 0);
488 489
	msix_set_enable(dev, 1);
	dev->msix_enabled = 1;
L
Linus Torvalds 已提交
490

491 492 493 494 495 496
	list_for_each_entry(entry, &dev->msi_list, list) {
		int vector = entry->msi_attrib.entry_nr;
		entry->masked = readl(base + vector * PCI_MSIX_ENTRY_SIZE +
					PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
	}

L
Linus Torvalds 已提交
497 498 499
	return 0;
}

500
/**
501
 * pci_msi_check_device - check whether MSI may be enabled on a device
502
 * @dev: pointer to the pci_dev data structure of MSI device function
503
 * @nvec: how many MSIs have been requested ?
504
 * @type: are we checking for MSI or MSI-X ?
505
 *
506
 * Look at global flags, the device itself, and its parent busses
507 508
 * to determine if MSI/-X are supported for the device. If MSI/-X is
 * supported return 0, else return an error code.
509
 **/
510
static int pci_msi_check_device(struct pci_dev* dev, int nvec, int type)
511 512
{
	struct pci_bus *bus;
513
	int ret;
514

515
	/* MSI must be globally enabled and supported by the device */
516 517 518
	if (!pci_msi_enable || !dev || dev->no_msi)
		return -EINVAL;

519 520 521 522 523 524 525 526
	/*
	 * You can't ask to have 0 or less MSIs configured.
	 *  a) it's stupid ..
	 *  b) the list manipulation code assumes nvec >= 1.
	 */
	if (nvec < 1)
		return -ERANGE;

527 528 529 530 531 532
	/* 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.
	 */
533 534 535 536
	for (bus = dev->bus; bus; bus = bus->parent)
		if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI)
			return -EINVAL;

537 538 539 540
	ret = arch_msi_check_device(dev, nvec, type);
	if (ret)
		return ret;

541 542 543
	if (!pci_find_capability(dev, type))
		return -EINVAL;

544 545 546
	return 0;
}

L
Linus Torvalds 已提交
547
/**
548 549 550
 * pci_enable_msi_block - configure device's MSI capability structure
 * @dev: device to configure
 * @nvec: number of interrupts to configure
L
Linus Torvalds 已提交
551
 *
552 553 554 555 556 557 558 559 560
 * Allocate IRQs for a device with the MSI capability.
 * This function returns a negative errno if an error occurs.  If it
 * is unable to allocate the number of interrupts requested, it returns
 * the number of interrupts it might be able to allocate.  If it successfully
 * allocates at least the number of interrupts requested, it returns 0 and
 * updates the @dev's irq member to the lowest new interrupt number; the
 * other interrupt numbers allocated to this device are consecutive.
 */
int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec)
L
Linus Torvalds 已提交
561
{
562 563 564 565 566 567 568 569 570 571
	int status, pos, maxvec;
	u16 msgctl;

	pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
	if (!pos)
		return -EINVAL;
	pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &msgctl);
	maxvec = 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1);
	if (nvec > maxvec)
		return maxvec;
L
Linus Torvalds 已提交
572

573
	status = pci_msi_check_device(dev, nvec, PCI_CAP_ID_MSI);
574 575
	if (status)
		return status;
L
Linus Torvalds 已提交
576

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

579
	/* Check whether driver already requested MSI-X irqs */
580
	if (dev->msix_enabled) {
581 582
		dev_info(&dev->dev, "can't enable MSI "
			 "(MSI-X already enabled)\n");
583
		return -EINVAL;
L
Linus Torvalds 已提交
584
	}
585 586

	status = msi_capability_init(dev, nvec);
L
Linus Torvalds 已提交
587 588
	return status;
}
589
EXPORT_SYMBOL(pci_enable_msi_block);
L
Linus Torvalds 已提交
590

591
void pci_msi_shutdown(struct pci_dev *dev)
L
Linus Torvalds 已提交
592
{
593 594 595
	struct msi_desc *desc;
	u32 mask;
	u16 ctrl;
L
Linus Torvalds 已提交
596

597
	if (!pci_msi_enable || !dev || !dev->msi_enabled)
E
Eric W. Biederman 已提交
598 599
		return;

600
	msi_set_enable(dev, 0);
601
	pci_intx_for_msi(dev, 1);
602
	dev->msi_enabled = 0;
603

604
	BUG_ON(list_empty(&dev->msi_list));
605 606 607 608
	desc = list_first_entry(&dev->msi_list, struct msi_desc, list);
	pci_read_config_word(dev, desc->msi_attrib.pos + PCI_MSI_FLAGS, &ctrl);
	mask = msi_capable_mask(ctrl);
	msi_mask_irq(desc, mask, ~mask);
609 610

	/* Restore dev->irq to its default pin-assertion irq */
611
	dev->irq = desc->msi_attrib.default_irq;
612
}
613

614 615 616 617 618 619 620 621 622 623
void pci_disable_msi(struct pci_dev* dev)
{
	struct msi_desc *entry;

	if (!pci_msi_enable || !dev || !dev->msi_enabled)
		return;

	pci_msi_shutdown(dev);

	entry = list_entry(dev->msi_list.next, struct msi_desc, list);
624
	if (entry->msi_attrib.is_msix)
625 626 627
		return;

	msi_free_irqs(dev);
L
Linus Torvalds 已提交
628
}
629
EXPORT_SYMBOL(pci_disable_msi);
L
Linus Torvalds 已提交
630

631
static int msi_free_irqs(struct pci_dev* dev)
L
Linus Torvalds 已提交
632
{
633
	struct msi_desc *entry, *tmp;
M
Michael Ellerman 已提交
634

635
	list_for_each_entry(entry, &dev->msi_list, list) {
636 637 638 639 640 641
		int i, nvec;
		if (!entry->irq)
			continue;
		nvec = 1 << entry->msi_attrib.multiple;
		for (i = 0; i < nvec; i++)
			BUG_ON(irq_has_action(entry->irq + i));
642
	}
L
Linus Torvalds 已提交
643

644
	arch_teardown_msi_irqs(dev);
L
Linus Torvalds 已提交
645

646
	list_for_each_entry_safe(entry, tmp, &dev->msi_list, list) {
647
		if (entry->msi_attrib.is_msix) {
648 649 650
			writel(1, entry->mask_base + entry->msi_attrib.entry_nr
				  * PCI_MSIX_ENTRY_SIZE
				  + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
651 652 653

			if (list_is_last(&entry->list, &dev->msi_list))
				iounmap(entry->mask_base);
654 655 656
		}
		list_del(&entry->list);
		kfree(entry);
L
Linus Torvalds 已提交
657 658 659 660 661
	}

	return 0;
}

662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678
/**
 * pci_msix_table_size - return the number of device's MSI-X table entries
 * @dev: pointer to the pci_dev data structure of MSI-X device function
 */
int pci_msix_table_size(struct pci_dev *dev)
{
	int pos;
	u16 control;

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

	pci_read_config_word(dev, msi_control_reg(pos), &control);
	return multi_msix_capable(control);
}

L
Linus Torvalds 已提交
679 680 681
/**
 * pci_enable_msix - configure device's MSI-X capability structure
 * @dev: pointer to the pci_dev data structure of MSI-X device function
682
 * @entries: pointer to an array of MSI-X entries
683
 * @nvec: number of MSI-X irqs requested for allocation by device driver
L
Linus Torvalds 已提交
684 685
 *
 * Setup the MSI-X capability structure of device function with the number
686
 * of requested irqs upon its software driver call to request for
L
Linus Torvalds 已提交
687 688
 * MSI-X mode enabled on its hardware device function. A return of zero
 * indicates the successful configuration of MSI-X capability structure
689
 * with new allocated MSI-X irqs. A return of < 0 indicates a failure.
L
Linus Torvalds 已提交
690
 * Or a return of > 0 indicates that driver request is exceeding the number
691 692
 * of irqs or MSI-X vectors available. Driver should use the returned value to
 * re-send its request.
L
Linus Torvalds 已提交
693 694 695
 **/
int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec)
{
696
	int status, nr_entries;
E
Eric W. Biederman 已提交
697
	int i, j;
L
Linus Torvalds 已提交
698

699
	if (!entries)
L
Linus Torvalds 已提交
700 701
 		return -EINVAL;

702 703 704 705
	status = pci_msi_check_device(dev, nvec, PCI_CAP_ID_MSIX);
	if (status)
		return status;

706
	nr_entries = pci_msix_table_size(dev);
L
Linus Torvalds 已提交
707
	if (nvec > nr_entries)
708
		return nr_entries;
L
Linus Torvalds 已提交
709 710 711 712 713 714 715 716 717 718

	/* 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 已提交
719
	WARN_ON(!!dev->msix_enabled);
720

721
	/* Check whether driver already requested for MSI irq */
722
   	if (dev->msi_enabled) {
723 724
		dev_info(&dev->dev, "can't enable MSI-X "
		       "(MSI IRQ already assigned)\n");
L
Linus Torvalds 已提交
725 726 727 728 729
		return -EINVAL;
	}
	status = msix_capability_init(dev, entries, nvec);
	return status;
}
730
EXPORT_SYMBOL(pci_enable_msix);
L
Linus Torvalds 已提交
731

732
static void msix_free_all_irqs(struct pci_dev *dev)
L
Linus Torvalds 已提交
733
{
734
	msi_free_irqs(dev);
735 736
}

737
void pci_msix_shutdown(struct pci_dev* dev)
738
{
739
	if (!pci_msi_enable || !dev || !dev->msix_enabled)
E
Eric W. Biederman 已提交
740 741
		return;

742
	msix_set_enable(dev, 0);
743
	pci_intx_for_msi(dev, 1);
744
	dev->msix_enabled = 0;
745 746 747 748 749 750 751
}
void pci_disable_msix(struct pci_dev* dev)
{
	if (!pci_msi_enable || !dev || !dev->msix_enabled)
		return;

	pci_msix_shutdown(dev);
752

753
	msix_free_all_irqs(dev);
L
Linus Torvalds 已提交
754
}
755
EXPORT_SYMBOL(pci_disable_msix);
L
Linus Torvalds 已提交
756 757

/**
758
 * msi_remove_pci_irq_vectors - reclaim MSI(X) irqs to unused state
L
Linus Torvalds 已提交
759 760
 * @dev: pointer to the pci_dev data structure of MSI(X) device function
 *
761
 * Being called during hotplug remove, from which the device function
762
 * is hot-removed. All previous assigned MSI/MSI-X irqs, if
L
Linus Torvalds 已提交
763 764 765 766 767 768 769 770
 * 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;

771 772
	if (dev->msi_enabled)
		msi_free_irqs(dev);
L
Linus Torvalds 已提交
773

774 775
	if (dev->msix_enabled)
		msix_free_all_irqs(dev);
L
Linus Torvalds 已提交
776 777
}

778 779 780 781
void pci_no_msi(void)
{
	pci_msi_enable = 0;
}
782

783 784 785 786 787 788 789
/**
 * pci_msi_enabled - is MSI enabled?
 *
 * Returns true if MSI has not been disabled by the command-line option
 * pci=nomsi.
 **/
int pci_msi_enabled(void)
790
{
791
	return pci_msi_enable;
792
}
793
EXPORT_SYMBOL(pci_msi_enabled);
794

795
void pci_msi_init_pci_dev(struct pci_dev *dev)
796
{
797
	INIT_LIST_HEAD(&dev->msi_list);
798
}