PCI Express设备驱动 (5)
和传统中断在系统初始化扫描PCI bus tree时就已自动为设备分配好中断号不同,MSI中断是在设备驱动程序初始化时调用pci_enable_msi() kernel API 时才分配中断号的。所以如果使用传统中断,在设备驱动程序中直接调用request_irq(pDev->irq, handler,...) 注册设备中断处理函数即可。而使用MSI中断的话,需先调用pci_enable_msi() 初始化设备MSI 结构,分配MSI中断号,并替换INTx中断号,再调用request_irq(pDev->irq, handler,...) 注册设备中断处理函数。除了卸载中断处理函数需要相应地调用pci_diable_msi()外,其他的处理完全相同。下面的Linux 内核代码详细描述了这一过程:
linux/drivers/pci/msi.c
/**
541 * pci_enable_msi - configure device's MSI capability structure
542 * @dev: pointer to the pci_dev data structure of MSI device function
543 *
544 * Setup the MSI capability structure of device function with
545 * a single MSI irq upon its software driver call to request for
546 * MSI mode enabled on its hardware device function. A return of zero
547 * indicates the successful setup of an entry zero with the new MSI
548 * irq or non-zero for otherwise.
549 **/
550 int pci_enable_msi(struct pci_dev* dev)
551 {
552 int status;
553
554 status = pci_msi_check_device(dev, 1, PCI_CAP_ID_MSI);
555 if (status)
556 return status;
557
558 WARN_ON(!!dev->msi_enabled);
559
560 /* Check whether driver already requested for MSI-X irqs */
561 if (dev->msix_enabled) {
562 printk(KERN_INFO "PCI: %s: Can't enable MSI. "
563 "Device already has MSI-X enabled\n",
564 pci_name(dev));
565 return -EINVAL;
566 }
567 status = msi_capability_init(dev); /*该函数配置设备配置空间的MSI结构并分配替换MSI中 断号 */
568 return status;
569 }
570 EXPORT_SYMBOL(pci_enable_msi);
571
linux/include/linux/msi.h
http://lxr.oss.org.cn/source/include/linux/msi.h?v=2.6.25#L6
linux/drivers/pci/msi.h
http://lxr.oss.org.cn/source/drivers/pci/msi.h?v=2.6.25#L16
/**
338 * msi_capability_init - configure device's MSI capability structure
339 * @dev: pointer to the pci_dev data structure of MSI device function
340 *
341 * Setup the MSI capability structure of device function with a single
342 * MSI irq, regardless of device function is capable of handling
343 * multiple messages. A return of zero indicates the successful setup
344 * of an entry zero with the new MSI irq or non-zero for otherwise.
345 **/
346 static int msi_capability_init(struct pci_dev *dev)
347 {
348 struct msi_desc *entry;
349 int pos, ret;
350 u16 control;
351
352 msi_set_enable(dev, 0); /* Ensure msi is disabled as I set it up */
353
354 pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
355 pci_read_config_word(dev, msi_control_reg(pos), &control);
356 /* MSI Entry Initialization */
357 entry = alloc_msi_entry();
358 if (!entry)
359 return -ENOMEM;
360
361 entry->msi_attrib.type = PCI_CAP_ID_MSI;
362 entry->msi_attrib.is_64 = is_64bit_address(control);
363 entry->msi_attrib.entry_nr = 0;
364 entry->msi_attrib.maskbit = is_mask_bit_support(control);
365 entry->msi_attrib.masked = 1;
366 entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */
367 entry->msi_attrib.pos = pos;
368 if (is_mask_bit_support(control)) {
369 entry->mask_base = (void __iomem *)(long)msi_mask_bits_reg(pos,
370 is_64bit_address(control));
371 }
372 entry->dev = dev;
373 if (entry->msi_attrib.maskbit) {
374 unsigned int maskbits, temp;
375 /* All MSIs are unmasked by default, Mask them all */
376 pci_read_config_dword(dev,
377 msi_mask_bits_reg(pos, is_64bit_address(control)),
378 &maskbits);
379 temp = (1 << multi_msi_capable(control));
380 temp = ((temp - 1) & ~temp);
381 maskbits |= temp;
382 pci_write_config_dword(dev,
383 msi_mask_bits_reg(pos, is_64bit_address(control)),
384 maskbits);
385 }
386 list_add_tail(&entry->list, &dev->msi_list);
387
388 /* Configure MSI capability structure */
389 ret = arch_setup_msi_irqs(dev, 1, PCI_CAP_ID_MSI);
390 if (ret) {
391 msi_free_irqs(dev);
392 return ret;
393 }
394
395 /* Set MSI enabled bits */
396 pci_intx_for_msi(dev, 0); // 设置配置空间的command寄存器,使能MSI中断.
397 msi_set_enable(dev, 1); // 设置配置空间的MSI寄存器,使能MSI中断.
398 dev->msi_enabled = 1;
399
400 dev->irq = entry->irq;
401 return 0;
402 }
42 int __attribute__ ((weak))
43 arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
44 {
45 struct msi_desc *entry;
46 int ret;
47
48 list_for_each_entry(entry, &dev->msi_list, list) {
49 ret = arch_setup_msi_irq(dev, entry);
50 if (ret)
51 return ret;
52 }
53
54 return 0;
55 }
// arch/arm/mach-iop13xx/msi.c, line 174
// arch/x86/kernel/io_apic_32.c, line 2564
// arch/x86/kernel/io_apic_64.c, line 2021
// arch/ia64/kernel/msi_ia64.c, line 150
2549 /*
2550 * IRQ Chip for MSI PCI/PCI-X/PCI-Express Devices,
2551 * which implement the MSI or MSI-X Capability Structure.
2552 */
2553 static struct irq_chip msi_chip = {
2554 .name = "PCI-MSI",
2555 .unmask = unmask_msi_irq,
2556 .mask = mask_msi_irq,
2557 .ack = ack_ioapic_irq,
2558 #ifdef CONFIG_SMP
2559 .set_affinity = set_msi_irq_affinity,
2560 #endif
2561 .retrigger = ioapic_retrigger_irq,
2562 };
2563
2564 int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc)
2565 {
2566 struct msi_msg msg;
2567 int irq, ret;
2568 irq = create_irq();
2569 if (irq < 0)
2570 return irq;
2571
2572 ret = msi_compose_msg(dev, irq, &msg);
2573 if (ret < 0) {
2574 destroy_irq(irq);
2575 return ret;
2576 }
2577
2578 set_irq_msi(irq, desc);
2579 write_msi_msg(irq, &msg);
2580
2581 set_irq_chip_and_handler_name(irq, &msi_chip, handle_edge_irq,
2582 "edge");
2583
2584 return 0;
2585 }
2484 /*
2485 * MSI message composition
2486 */
2487 #ifdef CONFIG_PCI_MSI
2488 static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_msg *msg)
2489 {
2490 int vector;
2491 unsigned dest;
2492
2493 vector = assign_irq_vector(irq);
2494 if (vector >= 0) {
2495 dest = cpu_mask_to_apicid(TARGET_CPUS);
2496
2497 msg->address_hi = MSI_ADDR_BASE_HI;
2498 msg->address_lo =
2499 MSI_ADDR_BASE_LO |
2500 ((INT_DEST_MODE == 0) ?
2501 MSI_ADDR_DEST_MODE_PHYSICAL:
2502 MSI_ADDR_DEST_MODE_LOGICAL) |
2503 ((INT_DELIVERY_MODE != dest_LowestPrio) ?
2504 MSI_ADDR_REDIRECTION_CPU:
2505 MSI_ADDR_REDIRECTION_LOWPRI) |
2506 MSI_ADDR_DEST_ID(dest);
2507
2508 msg->data =
2509 MSI_DATA_TRIGGER_EDGE |
2510 MSI_DATA_LEVEL_ASSERT |
2511 ((INT_DELIVERY_MODE != dest_LowestPrio) ?
2512 MSI_DATA_DELIVERY_FIXED:
2513 MSI_DATA_DELIVERY_LOWPRI) |
2514 MSI_DATA_VECTOR(vector);
2515 }
2516 return vector;
2517 }
// kernel/irq/chip.c, line 176
// include/linux/irq.h, line 392
169 /**
170 * set_irq_data - set irq type data for an irq
171 * @irq: Interrupt number
172 * @entry: Pointer to MSI descriptor data
173 *
174 * Set the hardware irq controller data for an irq
175 */
176 int set_irq_msi(unsigned int irq, struct msi_desc *entry)
177 {
178 struct irq_desc *desc;
179 unsigned long flags;
180
181 if (irq >= NR_IRQS) {
182 printk(KERN_ERR
183 "Trying to install msi data for IRQ%d\n", irq);
184 return -EINVAL;
185 }
186 desc = irq_desc + irq;
187 spin_lock_irqsave(&desc->lock, flags);
188 desc->msi_desc = entry;
189 if (entry)
190 entry->irq = irq;
191 spin_unlock_irqrestore(&desc->lock, flags);
192 return 0;
193 }
201 void write_msi_msg(unsigned int irq, struct msi_msg *msg)
202 {
203 struct msi_desc *entry = get_irq_msi(irq);
204 switch (entry->msi_attrib.type) {
205 case PCI_CAP_ID_MSI:
206 {
207 struct pci_dev *dev = entry->dev;
208 int pos = entry->msi_attrib.pos;
209
210 pci_write_config_dword(dev, msi_lower_address_reg(pos),
211 msg->address_lo);
212 if (entry->msi_attrib.is_64) {
213 pci_write_config_dword(dev, msi_upper_address_reg(pos),
214 msg->address_hi);
215 pci_write_config_word(dev, msi_data_reg(pos, 1),
216 msg->data);
217 } else {
218 pci_write_config_word(dev, msi_data_reg(pos, 0),
219 msg->data);
220 }
221 break;
222 }
223 case PCI_CAP_ID_MSIX:
224 {
225 void __iomem *base;
226 base = entry->mask_base +
227 entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE;
228
229 writel(msg->address_lo,
230 base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET);
231 writel(msg->address_hi,
232 base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET);
233 writel(msg->data, base + PCI_MSIX_ENTRY_DATA_OFFSET);
234 break;
235 }
236 default:
237 BUG();
238 }
239 entry->msg = *msg;
240 }
// arch/arm/mach-iop13xx/msi.c, line 128
// arch/x86/kernel/io_apic_32.c, line 2446
// arch/x86/kernel/io_apic_64.c, line 1900
// arch/ia64/kernel/irq_ia64.c, line 406
2443 /*
2444 * Dynamic irq allocate and deallocation
2445 */
2446 int create_irq(void)
2447 {
2448 /* Allocate an unused irq */
2449 int irq, new, vector = 0;
2450 unsigned long flags;
2451
2452 irq = -ENOSPC;
2453 spin_lock_irqsave(&vector_lock, flags);
2454 for (new = (NR_IRQS - 1); new >= 0; new--) {
2455 if (platform_legacy_irq(new))
2456 continue;
2457 if (irq_vector[new] != 0)
2458 continue;
2459 vector = __assign_irq_vector(new);
2460 if (likely(vector > 0))
2461 irq = new;
2462 break;
2463 }
2464 spin_unlock_irqrestore(&vector_lock, flags);
2465
2466 if (irq >= 0) {
2467 set_intr_gate(vector, interrupt[irq]);
2468 dynamic_irq_init(irq);
2469 }
2470 return irq;
2471 }
// arch/x86/kernel/io_apic_32.c, line 1227
// arch/x86/kernel/io_apic_64.c, line 746
// arch/ia64/kernel/irq_ia64.c, line 199
1194 /* irq_vectors is indexed by the sum of all RTEs in all I/O APICs. */
1195 static u8 irq_vector[NR_IRQ_VECTORS] __read_mostly = { FIRST_DEVICE_VECTOR , 0 };
1196
1197 static int __assign_irq_vector(int irq)
1198 {
1199 static int current_vector = FIRST_DEVICE_VECTOR, current_offset = 0;
1200 int vector, offset;
1201
1202 BUG_ON((unsigned)irq >= NR_IRQ_VECTORS);
1203
1204 if (irq_vector[irq] > 0)
1205 return irq_vector[irq];
1206
1207 vector = current_vector;
1208 offset = current_offset;
1209 next:
1210 vector += 8;
1211 if (vector >= FIRST_SYSTEM_VECTOR) {
1212 offset = (offset + 1) % 8;
1213 vector = FIRST_DEVICE_VECTOR + offset;
1214 }
1215 if (vector == current_vector)
1216 return -ENOSPC;
1217 if (test_and_set_bit(vector, used_vectors))
1218 goto next;
1219
1220 current_vector = vector;
1221 current_offset = offset;
1222 irq_vector[irq] = vector;
1223
1224 return vector;
1225 }
1226
1227 static int assign_irq_vector(int irq)
1228 {
1229 unsigned long flags;
1230 int vector;
1231
1232 spin_lock_irqsave(&vector_lock, flags);
1233 vector = __assign_irq_vector(irq);
1234 spin_unlock_irqrestore(&vector_lock, flags);
1235
1236 return vector;
1237 }
浙公网安备 33010602011771号