--- /dev/null
+/*
+ * File: msi.c
+ * Purpose: PCI Message Signaled Interrupt (MSI)
+ *
+ * Copyright (C) 2003-2004 Intel
+ * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
+ */
+
+#include <xen/config.h>
+#include <xen/lib.h>
+#include <xen/init.h>
+#include <xen/irq.h>
+#include <xen/delay.h>
+#include <xen/sched.h>
+#include <xen/acpi.h>
+#include <xen/errno.h>
+#include <xen/pci.h>
+#include <xen/pci_regs.h>
+#include <xen/keyhandler.h>
+#include <asm/io.h>
+#include <asm/smp.h>
+#include <asm/desc.h>
+#include <asm/msi.h>
+#include <asm/fixmap.h>
+#include <mach_apic.h>
+#include <io_ports.h>
+#include <public/physdev.h>
+
+extern int msi_irq_enable;
+
+/* PCI-dev list with MSI/MSIX capabilities */
+DEFINE_SPINLOCK(msi_pdev_lock);
+struct list_head msi_pdev_list;
+
+struct pci_dev *get_msi_pdev(u8 bus, u8 devfn)
+{
+ struct pci_dev *pdev = NULL;
+
+ list_for_each_entry(pdev, &msi_pdev_list, msi_dev_list)
+ if ( pdev->bus == bus && pdev->devfn == devfn )
+ return pdev;
+
+ return NULL;
+}
+
+/* bitmap indicate which fixed map is free */
+DEFINE_SPINLOCK(msix_fixmap_lock);
+DECLARE_BITMAP(msix_fixmap_pages, MAX_MSIX_PAGES);
+
+static int msix_fixmap_alloc(void)
+{
+ int i;
+ int rc = -1;
+
+ spin_lock(&msix_fixmap_lock);
+ for ( i = 0; i < MAX_MSIX_PAGES; i++ )
+ if ( !test_bit(i, &msix_fixmap_pages) )
+ break;
+ if ( i == MAX_MSIX_PAGES )
+ goto out;
+ rc = FIX_MSIX_IO_RESERV_BASE + i;
+ set_bit(i, &msix_fixmap_pages);
+
+ out:
+ spin_unlock(&msix_fixmap_lock);
+ return rc;
+}
+
+static void msix_fixmap_free(int idx)
+{
+ if ( idx < FIX_MSIX_IO_RESERV_BASE )
+ return;
+
+ spin_lock(&msix_fixmap_lock);
+ clear_bit(idx - FIX_MSIX_IO_RESERV_BASE, &msix_fixmap_pages);
+ spin_unlock(&msix_fixmap_lock);
+}
+
+/*
+ * MSI message composition
+ */
+static void msi_compose_msg(struct pci_dev *pdev, int vector,
+ struct msi_msg *msg)
+{
+ unsigned dest;
+ cpumask_t tmp;
+
+ tmp = TARGET_CPUS;
+ if ( vector )
+ {
+ dest = cpu_mask_to_apicid(tmp);
+
+ msg->address_hi = MSI_ADDR_BASE_HI;
+ msg->address_lo =
+ MSI_ADDR_BASE_LO |
+ ((INT_DEST_MODE == 0) ?
+ MSI_ADDR_DESTMODE_PHYS:
+ MSI_ADDR_DESTMODE_LOGIC) |
+ ((INT_DELIVERY_MODE != dest_LowestPrio) ?
+ MSI_ADDR_REDIRECTION_CPU:
+ MSI_ADDR_REDIRECTION_LOWPRI) |
+ MSI_ADDR_DEST_ID(dest);
+
+ msg->data =
+ MSI_DATA_TRIGGER_EDGE |
+ MSI_DATA_LEVEL_ASSERT |
+ ((INT_DELIVERY_MODE != dest_LowestPrio) ?
+ MSI_DATA_DELIVERY_FIXED:
+ MSI_DATA_DELIVERY_LOWPRI) |
+ MSI_DATA_VECTOR(vector);
+ }
+}
+
+void read_msi_msg(unsigned int irq, struct msi_msg *msg)
+{
+ struct msi_desc *entry = irq_desc[irq].msi_desc;
+
+ switch ( entry->msi_attrib.type )
+ {
+ case PCI_CAP_ID_MSI:
+ {
+ struct pci_dev *dev = entry->dev;
+ int pos = entry->msi_attrib.pos;
+ u16 data;
+ u8 bus = dev->bus;
+ u8 slot = PCI_SLOT(dev->devfn);
+ u8 func = PCI_FUNC(dev->devfn);
+
+ msg->address_lo = pci_conf_read32(bus, slot, func,
+ msi_lower_address_reg(pos));
+ if ( entry->msi_attrib.is_64 )
+ {
+ msg->address_hi = pci_conf_read32(bus, slot, func,
+ msi_upper_address_reg(pos));
+ data = pci_conf_read16(bus, slot, func, msi_data_reg(pos, 1));
+ }
+ else
+ {
+ msg->address_hi = 0;
+ data = pci_conf_read16(bus, slot, func, msi_data_reg(pos, 0));
+ }
+ 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();
+ }
+}
+
+static int set_vector_msi(struct msi_desc *entry)
+{
+ irq_desc_t *desc;
+ unsigned long flags;
+
+ if ( entry->vector >= NR_VECTORS )
+ {
+ dprintk(XENLOG_ERR, "Trying to install msi data for Vector %d\n",
+ entry->vector);
+ return -EINVAL;
+ }
+
+ desc = &irq_desc[entry->vector];
+ spin_lock_irqsave(&desc->lock, flags);
+ desc->msi_desc = entry;
+ spin_unlock_irqrestore(&desc->lock, flags);
+
+ return 0;
+}
+
+static int unset_vector_msi(int vector)
+{
+ irq_desc_t *desc;
+ unsigned long flags;
+
+ if ( vector >= NR_VECTORS )
+ {
+ dprintk(XENLOG_ERR, "Trying to uninstall msi data for Vector %d\n",
+ vector);
+ return -EINVAL;
+ }
+
+ desc = &irq_desc[vector];
+ spin_lock_irqsave(&desc->lock, flags);
+ desc->msi_desc = NULL;
+ spin_unlock_irqrestore(&desc->lock, flags);
+
+ return 0;
+}
+
+void write_msi_msg(unsigned int irq, struct msi_msg *msg)
+{
+ struct msi_desc *entry = irq_desc[irq].msi_desc;
+
+ switch ( entry->msi_attrib.type )
+ {
+ case PCI_CAP_ID_MSI:
+ {
+ struct pci_dev *dev = entry->dev;
+ int pos = entry->msi_attrib.pos;
+ u8 bus = dev->bus;
+ u8 slot = PCI_SLOT(dev->devfn);
+ u8 func = PCI_FUNC(dev->devfn);
+
+ pci_conf_write32(bus, slot, func, msi_lower_address_reg(pos),
+ msg->address_lo);
+ if ( entry->msi_attrib.is_64 )
+ {
+ pci_conf_write32(bus, slot, func, msi_upper_address_reg(pos),
+ msg->address_hi);
+ pci_conf_write16(bus, slot, func, msi_data_reg(pos, 1),
+ msg->data);
+ }
+ else
+ pci_conf_write16(bus, slot, func, msi_data_reg(pos, 0),
+ msg->data);
+ break;
+ }
+ case PCI_CAP_ID_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);
+ break;
+ }
+ default:
+ BUG();
+ }
+ entry->msg = *msg;
+}
+
+void set_msi_irq_affinity(unsigned int irq, cpumask_t mask)
+{
+ struct msi_msg msg;
+ unsigned int dest;
+
+ memset(&msg, 0, sizeof(msg));
+ cpus_and(mask, mask, cpu_online_map);
+ if ( cpus_empty(mask) )
+ mask = TARGET_CPUS;
+ dest = cpu_mask_to_apicid(mask);
+
+ read_msi_msg(irq, &msg);
+
+ msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK;
+ msg.address_lo |= MSI_ADDR_DEST_ID(dest);
+
+ write_msi_msg(irq, &msg);
+}
+
+static void msi_set_enable(struct pci_dev *dev, int enable)
+{
+ int pos;
+ u16 control;
+ u8 bus = dev->bus;
+ u8 slot = PCI_SLOT(dev->devfn);
+ u8 func = PCI_FUNC(dev->devfn);
+
+ pos = pci_find_cap_offset(bus, slot, func, PCI_CAP_ID_MSI);
+ if ( pos )
+ {
+ control = pci_conf_read16(bus, slot, func, pos + PCI_MSI_FLAGS);
+ control &= ~PCI_MSI_FLAGS_ENABLE;
+ if ( enable )
+ control |= PCI_MSI_FLAGS_ENABLE;
+ pci_conf_write16(bus, slot, func, pos + PCI_MSI_FLAGS, control);
+ }
+}
+
+void msix_set_enable(struct pci_dev *dev, int enable)
+{
+ int pos;
+ u16 control;
+ u8 bus = dev->bus;
+ u8 slot = PCI_SLOT(dev->devfn);
+ u8 func = PCI_FUNC(dev->devfn);
+
+ pos = pci_find_cap_offset(bus, slot, func, PCI_CAP_ID_MSIX);
+ if ( pos )
+ {
+ control = pci_conf_read16(bus, slot, func, pos + PCI_MSIX_FLAGS);
+ control &= ~PCI_MSIX_FLAGS_ENABLE;
+ if ( enable )
+ control |= PCI_MSIX_FLAGS_ENABLE;
+ pci_conf_write16(bus, slot, func, pos + PCI_MSIX_FLAGS, control);
+ }
+}
+
+static void msix_flush_writes(unsigned int irq)
+{
+ struct msi_desc *entry = irq_desc[irq].msi_desc;
+
+ BUG_ON(!entry || !entry->dev);
+ switch (entry->msi_attrib.type) {
+ case PCI_CAP_ID_MSI:
+ /* nothing to do */
+ break;
+ case PCI_CAP_ID_MSIX:
+ {
+ int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE +
+ PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET;
+ readl(entry->mask_base + offset);
+ break;
+ }
+ default:
+ BUG();
+ break;
+ }
+}
+
+static void msi_set_mask_bit(unsigned int irq, int flag)
+{
+ struct msi_desc *entry = irq_desc[irq].msi_desc;
+
+ BUG_ON(!entry || !entry->dev);
+ switch (entry->msi_attrib.type) {
+ case PCI_CAP_ID_MSI:
+ if (entry->msi_attrib.maskbit) {
+ int pos;
+ u32 mask_bits;
+ u8 bus = entry->dev->bus;
+ u8 slot = PCI_SLOT(entry->dev->devfn);
+ u8 func = PCI_FUNC(entry->dev->devfn);
+
+ pos = (long)entry->mask_base;
+ mask_bits = pci_conf_read32(bus, slot, func, pos);
+ mask_bits &= ~(1);
+ mask_bits |= flag;
+ pci_conf_write32(bus, slot, func, pos, mask_bits);
+ } else {
+ msi_set_enable(entry->dev, !flag);
+ }
+ 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);
+ readl(entry->mask_base + offset);
+ break;
+ }
+ default:
+ BUG();
+ break;
+ }
+ entry->msi_attrib.masked = !!flag;
+}
+
+void mask_msi_irq(unsigned int irq)
+{
+ msi_set_mask_bit(irq, 1);
+ msix_flush_writes(irq);
+}
+
+void unmask_msi_irq(unsigned int irq)
+{
+ msi_set_mask_bit(irq, 0);
+ msix_flush_writes(irq);
+}
+
+static struct msi_desc* alloc_msi_entry(void)
+{
+ struct msi_desc *entry;
+
+ entry = xmalloc(struct msi_desc);
+ if ( !entry )
+ return NULL;
+
+ INIT_LIST_HEAD(&entry->list);
+ entry->dev = NULL;
+
+ return entry;
+}
+
+static int setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc)
+{
+ struct msi_msg msg;
+
+ msi_compose_msg(dev, desc->vector, &msg);
+ set_vector_msi(desc);
+ write_msi_msg(desc->vector, &msg);
+
+ return 0;
+}
+
+static void teardown_msi_vector(int vector)
+{
+ unset_vector_msi(vector);
+}
+
+static void msi_free_vector(int vector)
+{
+ struct msi_desc *entry;
+
+ entry = irq_desc[vector].msi_desc;
+
+ teardown_msi_vector(vector);
+
+ if ( entry->msi_attrib.type == PCI_CAP_ID_MSIX )
+ {
+ unsigned long start;
+
+ writel(1, entry->mask_base + entry->msi_attrib.entry_nr
+ * PCI_MSIX_ENTRY_SIZE
+ + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
+
+ start = (unsigned long)entry->mask_base & ~(PAGE_SIZE - 1);
+ msix_fixmap_free(virt_to_fix(start));
+ destroy_xen_mappings(start, start + PAGE_SIZE);
+ }
+ list_del(&entry->list);
+ xfree(entry);
+}
+
+void msi_free_vectors(struct pci_dev* dev)
+{
+ struct msi_desc *entry, *tmp;
+
+ list_for_each_entry_safe( entry, tmp, &dev->msi_list, list )
+ msi_free_vector(entry->vector);
+}
+
+static struct msi_desc *find_msi_entry(struct pci_dev *dev,
+ int vector, int cap_id)
+{
+ struct msi_desc *entry;
+
+ list_for_each_entry( entry, &dev->msi_list, list )
+ {
+ if ( entry->msi_attrib.type == cap_id &&
+ (vector == -1 || entry->vector == vector) )
+ return entry;
+ }
+
+ return NULL;
+}
+
+/**
+ * msi_capability_init - 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 a single
+ * MSI irq, regardless of device function is capable of handling
+ * multiple messages. A return of zero indicates the successful setup
+ * of an entry zero with the new MSI irq or non-zero for otherwise.
+ **/
+static int msi_capability_init(struct pci_dev *dev, int vector)
+{
+ struct msi_desc *entry;
+ int pos, ret;
+ u16 control;
+ u8 bus = dev->bus;
+ u8 slot = PCI_SLOT(dev->devfn);
+ u8 func = PCI_FUNC(dev->devfn);
+
+ pos = pci_find_cap_offset(bus, slot, func, PCI_CAP_ID_MSI);
+ control = pci_conf_read16(bus, slot, func, msi_control_reg(pos));
+ /* MSI Entry Initialization */
+ msi_set_enable(dev, 0); /* Ensure msi is disabled as I set it up */
+
+ entry = alloc_msi_entry();
+ if ( !entry )
+ return -ENOMEM;
+
+ entry->msi_attrib.type = PCI_CAP_ID_MSI;
+ entry->msi_attrib.is_64 = is_64bit_address(control);
+ entry->msi_attrib.entry_nr = 0;
+ entry->msi_attrib.maskbit = is_mask_bit_support(control);
+ entry->msi_attrib.masked = 1;
+ entry->msi_attrib.pos = pos;
+ entry->vector = vector;
+ if ( is_mask_bit_support(control) )
+ entry->mask_base = (void __iomem *)(long)msi_mask_bits_reg(pos,
+ is_64bit_address(control));
+ entry->dev = dev;
+ if ( entry->msi_attrib.maskbit )
+ {
+ unsigned int maskbits, temp;
+ /* All MSIs are unmasked by default, Mask them all */
+ maskbits = pci_conf_read32(bus, slot, func,
+ msi_mask_bits_reg(pos, is_64bit_address(control)));
+ temp = (1 << multi_msi_capable(control));
+ temp = ((temp - 1) & ~temp);
+ maskbits |= temp;
+ pci_conf_write32(bus, slot, func,
+ msi_mask_bits_reg(pos, is_64bit_address(control)),
+ maskbits);
+ }
+ list_add_tail(&entry->list, &dev->msi_list);
+
+ /* Configure MSI capability structure */
+ ret = setup_msi_irq(dev, entry);
+ if ( ret )
+ {
+ msi_free_vector(vector);
+ return ret;
+ }
+
+ /* Restore the original MSI enabled bits */
+ pci_conf_write16(bus, slot, func, msi_control_reg(pos), control);
+
+ return 0;
+}
+
+static u64 pci_resource_start(struct pci_dev *dev, u8 bar_index)
+{
+ unsigned long bar_base;
+ u8 bus = dev->bus;
+ u8 slot = PCI_SLOT(dev->devfn);
+ u8 func = PCI_FUNC(dev->devfn);
+
+ bar_base = pci_conf_read32(bus, slot, func,
+ PCI_BASE_ADDRESS_0 + 4 * bar_index);
+ if ( bar_base & PCI_BASE_ADDRESS_MEM_TYPE_64 )
+ {
+ bar_base <<= 32;
+ bar_base += pci_conf_read32(bus, slot, func,
+ PCI_BASE_ADDRESS_0 + 4 * (bar_index + 1));
+ }
+
+ return bar_base;
+}
+
+/**
+ * msix_capability_init - configure device's MSI-X capability
+ * @dev: pointer to the pci_dev data structure of MSI-X device function
+ * @entries: pointer to an array of struct msix_entry entries
+ * @nvec: number of @entries
+ *
+ * Setup the MSI-X capability structure of device function with a
+ * 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.
+ **/
+static int msix_capability_init(struct pci_dev *dev, int vector, int entry_nr)
+{
+ struct msi_desc *entry;
+ int pos;
+ u16 control;
+ unsigned long phys_addr;
+ u32 table_offset;
+ u8 bir;
+ void __iomem *base;
+ int idx;
+ u8 bus = dev->bus;
+ u8 slot = PCI_SLOT(dev->devfn);
+ u8 func = PCI_FUNC(dev->devfn);
+
+ pos = pci_find_cap_offset(bus, slot, func, PCI_CAP_ID_MSIX);
+ control = pci_conf_read16(bus, slot, func, msix_control_reg(pos));
+ msix_set_enable(dev, 0);/* Ensure msix is disabled as I set it up */
+
+ /* MSI-X Table Initialization */
+ entry = alloc_msi_entry();
+ if ( !entry )
+ return -ENOMEM;
+
+ /* Request & Map MSI-X table region */
+ table_offset = pci_conf_read32(bus, slot, func, msix_table_offset_reg(pos));
+ bir = (u8)(table_offset & PCI_MSIX_FLAGS_BIRMASK);
+ table_offset &= ~PCI_MSIX_FLAGS_BIRMASK;
+ phys_addr = pci_resource_start(dev, bir) + table_offset;
+ idx = msix_fixmap_alloc();
+ if ( idx < 0 )
+ {
+ xfree(entry);
+ return -ENOMEM;
+ }
+ set_fixmap_nocache(idx, phys_addr);
+ base = (void *)(fix_to_virt(idx) + (phys_addr & ((1UL << PAGE_SHIFT) - 1)));
+
+ entry->msi_attrib.type = PCI_CAP_ID_MSIX;
+ entry->msi_attrib.is_64 = 1;
+ entry->msi_attrib.entry_nr = entry_nr;
+ entry->msi_attrib.maskbit = 1;
+ entry->msi_attrib.masked = 1;
+ entry->msi_attrib.pos = pos;
+ entry->vector = vector;
+ entry->dev = dev;
+ entry->mask_base = base;
+
+ list_add_tail(&entry->list, &dev->msi_list);
+
+ setup_msi_irq(dev, entry);
+
+ /* Set MSI-X enabled bits */
+ pci_conf_write16(bus, slot, func, msix_control_reg(pos), control);
+
+ return 0;
+}
+
+/**
+ * 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
+ * a single MSI irq upon its software driver call to request for
+ * MSI mode enabled on its hardware device function. A return of zero
+ * indicates the successful setup of an entry zero with the new MSI
+ * irq or non-zero for otherwise.
+ **/
+static int __pci_enable_msi(u8 bus, u8 devfn, int vector)
+{
+ int status;
+ struct pci_dev *dev;
+
+ dev = get_msi_pdev(bus, devfn);
+ if ( !dev )
+ {
+ dev = xmalloc(struct pci_dev);
+ if ( !dev )
+ return -ENOMEM;
+ dev->bus = bus;
+ dev->devfn = devfn;
+ INIT_LIST_HEAD(&dev->msi_list);
+ }
+
+ if ( find_msi_entry(dev, vector, PCI_CAP_ID_MSI) )
+ {
+ dprintk(XENLOG_WARNING, "vector %d has already mapped to MSI on device \
+ %02x:%02x.%01x.\n", vector, bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
+ return 0;
+ }
+
+ status = msi_capability_init(dev, vector);
+
+ if ( dev != get_msi_pdev(bus, devfn) )
+ {
+ spin_lock(&msi_pdev_lock);
+ list_add_tail(&dev->msi_dev_list, &msi_pdev_list);
+ spin_unlock(&msi_pdev_lock);
+ }
+
+ return status;
+}
+
+static void __pci_disable_msi(int vector)
+{
+ struct msi_desc *entry;
+ struct pci_dev *dev;
+ int pos;
+ u16 control;
+ u8 bus, slot, func;
+
+ entry = irq_desc[vector].msi_desc;
+ dev = entry->dev;
+ bus = dev->bus;
+ slot = PCI_SLOT(dev->devfn);
+ func = PCI_FUNC(dev->devfn);
+
+ pos = pci_find_cap_offset(bus, slot, func, PCI_CAP_ID_MSI);
+ control = pci_conf_read16(bus, slot, func, msi_control_reg(pos));
+ msi_set_enable(dev, 0);
+
+ BUG_ON(list_empty(&dev->msi_list));
+
+ msi_free_vector(vector);
+
+ pci_conf_write16(bus, slot, func, msi_control_reg(pos), control);
+}
+
+/**
+ * pci_enable_msix - configure device's MSI-X capability structure
+ * @dev: pointer to the pci_dev data structure of MSI-X device function
+ * @entries: pointer to an array of MSI-X entries
+ * @nvec: number of MSI-X irqs requested for allocation by device driver
+ *
+ * Setup the MSI-X capability structure of device function with the number
+ * of requested irqs upon its software driver call to request for
+ * MSI-X mode enabled on its hardware device function. A return of zero
+ * indicates the successful configuration of MSI-X capability structure
+ * with new allocated MSI-X irqs. A return of < 0 indicates a failure.
+ * Or a return of > 0 indicates that driver request is exceeding the number
+ * of irqs available. Driver should use the returned value to re-send
+ * its request.
+ **/
+static int __pci_enable_msix(u8 bus, u8 devfn, int vector, int entry_nr)
+{
+ int status, pos, nr_entries;
+ struct pci_dev *dev;
+ u16 control;
+ u8 slot = PCI_SLOT(devfn);
+ u8 func = PCI_FUNC(devfn);
+
+ pos = pci_find_cap_offset(bus, slot, func, PCI_CAP_ID_MSIX);
+ control = pci_conf_read16(bus, slot, func, msi_control_reg(pos));
+ nr_entries = multi_msix_capable(control);
+ if (entry_nr > nr_entries)
+ return -EINVAL;
+
+ /* Check whether driver already requested for MSI-X irqs */
+ dev = get_msi_pdev(bus, devfn);
+
+ if ( !dev )
+ {
+ dev = xmalloc(struct pci_dev);
+ if ( !dev )
+ return -ENOMEM;
+ dev->bus = bus;
+ dev->devfn = devfn;
+ INIT_LIST_HEAD(&dev->msi_list);
+ }
+
+ if ( find_msi_entry(dev, vector, PCI_CAP_ID_MSIX) )
+ {
+ dprintk(XENLOG_WARNING, "vector %d has already mapped to MSIX on \
+ device %02x:%02x.%01x.\n", vector, bus,
+ PCI_SLOT(devfn), PCI_FUNC(devfn));
+ return 0;
+ }
+
+ status = msix_capability_init(dev, vector, entry_nr);
+
+ if ( dev != get_msi_pdev(bus, devfn) )
+ {
+ spin_lock(&msi_pdev_lock);
+ list_add_tail(&dev->msi_dev_list, &msi_pdev_list);
+ spin_unlock(&msi_pdev_lock);
+ }
+
+ return status;
+}
+
+static void __pci_disable_msix(int vector)
+{
+ struct msi_desc *entry;
+ struct pci_dev *dev;
+ int pos;
+ u16 control;
+ u8 bus, slot, func;
+
+ entry = irq_desc[vector].msi_desc;
+ dev = entry->dev;
+ bus = dev->bus;
+ slot = PCI_SLOT(dev->devfn);
+ func = PCI_FUNC(dev->devfn);
+
+ pos = pci_find_cap_offset(bus, slot, func, PCI_CAP_ID_MSIX);
+ control = pci_conf_read16(bus, slot, func, msix_control_reg(pos));
+ msi_set_enable(dev, 0);
+
+ BUG_ON(list_empty(&dev->msi_list));
+
+ msi_free_vector(vector);
+
+ pci_conf_write16(bus, slot, func, msix_control_reg(pos), control);
+}
+
+int pci_enable_msi(u8 bus, u8 devfn, int vector, int entry_nr, int msi)
+{
+ if ( msi )
+ return __pci_enable_msi(bus, devfn, vector);
+ else
+ return __pci_enable_msix(bus, devfn, vector, entry_nr);
+}
+
+void pci_disable_msi(int vector)
+{
+ irq_desc_t *desc;
+
+ desc = &irq_desc[vector];
+ if ( desc->msi_desc->msi_attrib.type == PCI_CAP_ID_MSI )
+ __pci_disable_msi(vector);
+ else if ( desc->msi_desc->msi_attrib.type == PCI_CAP_ID_MSIX )
+ __pci_disable_msix(vector);
+}
+
+void pci_cleanup_msi(struct pci_dev *dev)
+{
+ msi_free_vectors(dev);
+}
+
--- /dev/null
+#ifndef __ASM_MSI_H
+#define __ASM_MSI_H
+
+#include <xen/cpumask.h>
+#include <asm/irq.h>
+/*
+ * Constants for Intel APIC based MSI messages.
+ */
+
+/*
+ * Shifts for MSI data
+ */
+
+#define MSI_DATA_VECTOR_SHIFT 0
+#define MSI_DATA_VECTOR_MASK 0x000000ff
+#define MSI_DATA_VECTOR(v) (((v) << MSI_DATA_VECTOR_SHIFT) & MSI_DATA_VECTOR_MASK)
+
+#define MSI_DATA_DELIVERY_MODE_SHIFT 8
+#define MSI_DATA_DELIVERY_FIXED (0 << MSI_DATA_DELIVERY_MODE_SHIFT)
+#define MSI_DATA_DELIVERY_LOWPRI (1 << MSI_DATA_DELIVERY_MODE_SHIFT)
+
+#define MSI_DATA_LEVEL_SHIFT 14
+#define MSI_DATA_LEVEL_DEASSERT (0 << MSI_DATA_LEVEL_SHIFT)
+#define MSI_DATA_LEVEL_ASSERT (1 << MSI_DATA_LEVEL_SHIFT)
+
+#define MSI_DATA_TRIGGER_SHIFT 15
+#define MSI_DATA_TRIGGER_EDGE (0 << MSI_DATA_TRIGGER_SHIFT)
+#define MSI_DATA_TRIGGER_LEVEL (1 << MSI_DATA_TRIGGER_SHIFT)
+
+/*
+ * Shift/mask fields for msi address
+ */
+
+#define MSI_ADDR_BASE_HI 0
+#define MSI_ADDR_BASE_LO 0xfee00000
+#define MSI_ADDR_HEADER MSI_ADDR_BASE_LO
+
+#define MSI_ADDR_DESTMODE_SHIFT 2
+#define MSI_ADDR_DESTMODE_PHYS (0 << MSI_ADDR_DESTMODE_SHIFT)
+#define MSI_ADDR_DESTMODE_LOGIC (1 << MSI_ADDR_DESTMODE_SHIFT)
+
+#define MSI_ADDR_REDIRECTION_SHIFT 3
+#define MSI_ADDR_REDIRECTION_CPU (0 << MSI_ADDR_REDIRECTION_SHIFT)
+#define MSI_ADDR_REDIRECTION_LOWPRI (1 << MSI_ADDR_REDIRECTION_SHIFT)
+
+#define MSI_ADDR_DEST_ID_SHIFT 12
+#define MSI_ADDR_DEST_ID_MASK 0x00ffff0
+#define MSI_ADDR_DEST_ID(dest) (((dest) << MSI_ADDR_DEST_ID_SHIFT) & MSI_ADDR_DEST_ID_MASK)
+
+/* MAX fixed pages reserved for mapping MSIX tables. */
+#if defined(__x86_64__)
+#define MAX_MSIX_PAGES 512
+#else
+#define MAX_MSIX_PAGES 32
+#endif
+
+struct msi_msg {
+ u32 address_lo; /* low 32 bits of msi message address */
+ u32 address_hi; /* high 32 bits of msi message address */
+ u32 data; /* 16 bits of msi message data */
+};
+
+/* Helper functions */
+extern void mask_msi_irq(unsigned int irq);
+extern void unmask_msi_irq(unsigned int irq);
+extern void read_msi_msg(unsigned int irq, struct msi_msg *msg);
+extern void write_msi_msg(unsigned int irq, struct msi_msg *msg);
+extern void set_msi_irq_affinity(unsigned int irq, cpumask_t mask);
+extern int pci_enable_msi(u8 bus, u8 devfn, int vector, int entry_nr, int msi);
+extern void pci_disable_msi(int vector);
+
+struct msi_desc {
+ struct {
+ __u8 type : 5; /* {0: unused, 5h:MSI, 11h:MSI-X} */
+ __u8 maskbit : 1; /* mask-pending bit supported ? */
+ __u8 masked : 1;
+ __u8 is_64 : 1; /* Address size: 0=32bit 1=64bit */
+ __u8 pos; /* Location of the msi capability */
+ __u16 entry_nr; /* specific enabled entry */
+ }msi_attrib;
+
+ struct list_head list;
+
+ void __iomem *mask_base;
+ struct pci_dev *dev;
+ int vector;
+
+ /* Last set MSI message */
+ struct msi_msg msg;
+};
+
+/*
+ * Assume the maximum number of hot plug slots supported by the system is about
+ * ten. The worstcase is that each of these slots is hot-added with a device,
+ * which has two MSI/MSI-X capable functions. To avoid any MSI-X driver, which
+ * attempts to request all available vectors, NR_HP_RESERVED_VECTORS is defined
+ * as below to ensure at least one message is assigned to each detected MSI/
+ * MSI-X device function.
+ */
+#define NR_HP_RESERVED_VECTORS 20
+
+extern int vector_irq[NR_VECTORS];
+
+/*
+ * MSI-X Address Register
+ */
+#define PCI_MSIX_FLAGS_QSIZE 0x7FF
+#define PCI_MSIX_FLAGS_ENABLE (1 << 15)
+#define PCI_MSIX_FLAGS_BIRMASK (7 << 0)
+#define PCI_MSIX_FLAGS_BITMASK (1 << 0)
+
+#define PCI_MSIX_ENTRY_SIZE 16
+#define PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET 0
+#define PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET 4
+#define PCI_MSIX_ENTRY_DATA_OFFSET 8
+#define PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET 12
+
+#define msi_control_reg(base) (base + PCI_MSI_FLAGS)
+#define msi_lower_address_reg(base) (base + PCI_MSI_ADDRESS_LO)
+#define msi_upper_address_reg(base) (base + PCI_MSI_ADDRESS_HI)
+#define msi_data_reg(base, is64bit) \
+ ( (is64bit == 1) ? base+PCI_MSI_DATA_64 : base+PCI_MSI_DATA_32 )
+#define msi_mask_bits_reg(base, is64bit) \
+ ( (is64bit == 1) ? base+PCI_MSI_MASK_BIT : base+PCI_MSI_MASK_BIT-4)
+#define msi_disable(control) control &= ~PCI_MSI_FLAGS_ENABLE
+#define multi_msi_capable(control) \
+ (1 << ((control & PCI_MSI_FLAGS_QMASK) >> 1))
+#define multi_msi_enable(control, num) \
+ control |= (((num >> 1) << 4) & PCI_MSI_FLAGS_QSIZE);
+#define is_64bit_address(control) (!!(control & PCI_MSI_FLAGS_64BIT))
+#define is_mask_bit_support(control) (!!(control & PCI_MSI_FLAGS_MASKBIT))
+#define msi_enable(control, num) multi_msi_enable(control, num); \
+ control |= PCI_MSI_FLAGS_ENABLE
+
+#define msix_control_reg(base) (base + PCI_MSIX_FLAGS)
+#define msix_table_offset_reg(base) (base + 0x04)
+#define msix_pba_offset_reg(base) (base + 0x08)
+#define msix_enable(control) control |= PCI_MSIX_FLAGS_ENABLE
+#define msix_disable(control) control &= ~PCI_MSIX_FLAGS_ENABLE
+#define msix_table_size(control) ((control & PCI_MSIX_FLAGS_QSIZE)+1)
+#define multi_msix_capable msix_table_size
+#define msix_unmask(address) (address & ~PCI_MSIX_FLAGS_BITMASK)
+#define msix_mask(address) (address | PCI_MSIX_FLAGS_BITMASK)
+#define msix_is_pending(address) (address & PCI_MSIX_FLAGS_PENDMASK)
+
+/*
+ * MSI Defined Data Structures
+ */
+#define MSI_ADDRESS_HEADER 0xfee
+#define MSI_ADDRESS_HEADER_SHIFT 12
+#define MSI_ADDRESS_HEADER_MASK 0xfff000
+#define MSI_ADDRESS_DEST_ID_MASK 0xfff0000f
+#define MSI_TARGET_CPU_MASK 0xff
+#define MSI_TARGET_CPU_SHIFT 12
+#define MSI_DELIVERY_MODE 0
+#define MSI_LEVEL_MODE 1 /* Edge always assert */
+#define MSI_TRIGGER_MODE 0 /* MSI is edge sensitive */
+#define MSI_PHYSICAL_MODE 0
+#define MSI_LOGICAL_MODE 1
+#define MSI_REDIRECTION_HINT_MODE 0
+
+#define __LITTLE_ENDIAN_BITFIELD 1
+
+struct msg_data {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ __u32 vector : 8;
+ __u32 delivery_mode : 3; /* 000b: FIXED | 001b: lowest prior */
+ __u32 reserved_1 : 3;
+ __u32 level : 1; /* 0: deassert | 1: assert */
+ __u32 trigger : 1; /* 0: edge | 1: level */
+ __u32 reserved_2 : 16;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ __u32 reserved_2 : 16;
+ __u32 trigger : 1; /* 0: edge | 1: level */
+ __u32 level : 1; /* 0: deassert | 1: assert */
+ __u32 reserved_1 : 3;
+ __u32 delivery_mode : 3; /* 000b: FIXED | 001b: lowest prior */
+ __u32 vector : 8;
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+} __attribute__ ((packed));
+
+struct msg_address {
+ union {
+ struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ __u32 reserved_1 : 2;
+ __u32 dest_mode : 1; /*0:physic | 1:logic */
+ __u32 redirection_hint: 1; /*0: dedicated CPU
+ 1: lowest priority */
+ __u32 reserved_2 : 4;
+ __u32 dest_id : 24; /* Destination ID */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ __u32 dest_id : 24; /* Destination ID */
+ __u32 reserved_2 : 4;
+ __u32 redirection_hint: 1; /*0: dedicated CPU
+ 1: lowest priority */
+ __u32 dest_mode : 1; /*0:physic | 1:logic */
+ __u32 reserved_1 : 2;
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+ }u;
+ __u32 value;
+ }lo_address;
+ __u32 hi_address;
+} __attribute__ ((packed));
+
+#endif /* __ASM_MSI_H */