x86: msi: refactor pci_msi_enable() Pulled out the setting-of-the-addr-and-data into its own function, and clarified the difference between msi_ready and msix_ready. Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
diff --git a/kern/arch/x86/msi.c b/kern/arch/x86/msi.c index 06e8319..1cefe07 100644 --- a/kern/arch/x86/msi.c +++ b/kern/arch/x86/msi.c
@@ -140,6 +140,47 @@ return Msidmode * deliv_mode | ((unsigned int)vec & 0xff); } +/* TODO: do we need to be careful of reserved bits? SDM says to preserve those + * fields on write. */ +static void __msi_set_addr_data(struct pci_device *p, int cap) +{ + unsigned int f, datao; + + /* read it, clear out the Mmesgmsk bits. + * This means that there will be no multiple + * messages enabled. + */ + f = pcidev_read16(p, cap + 2) & ~Mmesgmsk; + + /* Data begins at 8 bytes in. */ + datao = 8; + pcidev_write32(p, cap + 4, p->msi_msg_addr_lo); + + /* And even if it's 64-bit capable, we do nothing with + * the high order bits. If it is 64-bit we need to offset + * datao (data offset) by 4 (i.e. another 32 bits) + */ + if (f & Cap64) { + datao += 4; + pcidev_write32(p, cap + 8, 0); + } + + pcidev_write16(p, cap + datao, p->msi_msg_data); + + /* If we have the option of masking the vectors, + * blow all the masks to 0. It's a 32-bit mask. + */ + if (f & Vmask) + pcidev_write32(p, cap + datao + 4, 0); + + /* Now write the control bits back, with the Mmesg mask (which is a + * power of 2) set to 0 (meaning one vector only). Note we still + * haven't enabled MSI. Will do that when we unmask. According to the + * spec, we're not supposed to use the Msienable bit to mask the IRQ, + * though I don't see how we can mask on non-Vmask-supported HW. */ + pcidev_write16(p, cap + 2, f); +} + /* see section 6.8.1 of the pci spec. */ /* Set up a single function on a single device. * We need to take the vec, bust it up into bits, @@ -148,7 +189,7 @@ */ int pci_msi_enable(struct pci_device *p, uint64_t vec) { - unsigned int c, f, datao; + unsigned int c; spin_lock_irqsave(&p->lock); if (p->msix_ready) { @@ -156,15 +197,15 @@ spin_unlock_irqsave(&p->lock); return -1; } + /* msi_ready means "has an IRQ vector assigned, loaded, and masked". + * We're only allowing one MSI vector per device. In comparison, + * msix_ready means "has all the stuff set up for MSI-X so you can get + * some IRQ vector, load the msix_entry, and go." */ if (p->msi_ready) { - /* only allowing one enable of MSI per device (not supporting - * multiple vectors) */ printk("MSI: MSI is already enabled, aborting\n"); spin_unlock_irqsave(&p->lock); return -1; } - p->msi_ready = TRUE; - /* Get the offset of the MSI capability in the function's config space. */ c = msicap(p); @@ -172,51 +213,15 @@ spin_unlock_irqsave(&p->lock); return -1; } - - /* read it, clear out the Mmesgmsk bits. - * This means that there will be no multiple - * messages enabled. - */ - f = pcidev_read16(p, c + 2) & ~Mmesgmsk; - if (msi_blacklist(p) != 0) { spin_unlock_irqsave(&p->lock); return -1; } - - /* Data begins at 8 bytes in. */ - datao = 8; p->msi_msg_addr_lo = msi_make_addr_lo(vec); - printd("Write to %d %08lx \n",c + 4, p->msi_msg_addr_lo); - pcidev_write32(p, c + 4, p->msi_msg_addr_lo); - - /* And even if it's 64-bit capable, we do nothing with - * the high order bits. If it is 64-bit we need to offset - * datao (data offset) by 4 (i.e. another 32 bits) - */ - if(f & Cap64){ - datao += 4; - pcidev_write32(p, c + 8, 0); - } p->msi_msg_addr_hi = 0; - p->msi_msg_data = msi_make_data(vec); - printd("Write data %d %04x\n", c + datao, p->msi_msg_data); - pcidev_write16(p, c + datao, p->msi_msg_data); - - /* If we have the option of masking the vectors, - * blow all the masks to 0. It's a 32-bit mask. - */ - if(f & Vmask) - pcidev_write32(p, c + datao + 4, 0); - - /* Now write the control bits back, with the Mmesg mask (which is a - * power of 2) set to 0 (meaning one vector only). Note we still - * haven't enabled MSI. Will do that when we unmask. According to the - * spec, we're not supposed to use the Msienable bit to mask the IRQ, - * though I don't see how we can mask on non-Vmask-supported HW. */ - printd("write @ %d %04lx\n",c + 2, f); - pcidev_write16(p, c + 2, f); + __msi_set_addr_data(p, c); + p->msi_ready = true; spin_unlock_irqsave(&p->lock); return 0; }