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