};
struct ioapic_alloc_info {
- int pin;
- int node;
- u32 trigger : 1;
- u32 polarity : 1;
- u32 valid : 1;
- struct IO_APIC_route_entry *entry;
+ int pin;
+ int node;
+ u32 trigger : 1;
+ u32 polarity : 1;
+ u32 valid : 1;
};
struct uv_alloc_info {
#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_DELIVERY_MODE_MASK (3 << MSI_DATA_DELIVERY_MODE_SHIFT)
#define MSI_DATA_LEVEL_SHIFT 14
#define MSI_DATA_LEVEL_DEASSERT (0 << MSI_DATA_LEVEL_SHIFT)
#define MSI_ADDR_DEST_MODE_SHIFT 2
#define MSI_ADDR_DEST_MODE_PHYSICAL (0 << MSI_ADDR_DEST_MODE_SHIFT)
#define MSI_ADDR_DEST_MODE_LOGICAL (1 << MSI_ADDR_DEST_MODE_SHIFT)
+#define MSI_ADDR_DEST_MODE_MASK (1 << MSI_DATA_DELIVERY_MODE_SHIFT)
#define MSI_ADDR_REDIRECTION_SHIFT 3
#define MSI_ADDR_REDIRECTION_CPU (0 << MSI_ADDR_REDIRECTION_SHIFT)
#include <linux/jiffies.h> /* time_after() */
#include <linux/slab.h>
#include <linux/memblock.h>
+#include <linux/msi.h>
#include <asm/irqdomain.h>
#include <asm/io.h>
#include <asm/setup.h>
#include <asm/irq_remapping.h>
#include <asm/hw_irq.h>
+#include <asm/msidef.h>
#include <asm/apic.h>
eoi_ioapic_pin(data->entry.vector, data);
}
+static void mp_swizzle_msi_dest_bits(struct irq_data *irq_data,
+ struct IO_APIC_route_entry *rte)
+{
+ struct msi_msg msg;
+ u32 *entry = (u32 *)rte;
+
+ /*
+ * They're in a bit of a random order for historical reasons, but
+ * the I/OAPIC is just a device for turning interrupt lines into
+ * MSIs, and various bits of the MSI addr/data are just swizzled
+ * into/from the bits of Redirection Table Entry. So let the
+ * upstream irqdomain (be it interrupt remapping or otherwise)
+ * compose the MSI message, and we'll shift the bits into the
+ * appropriate place in the RTE.
+ */
+ irq_chip_compose_msi_msg(irq_data, &msg);
+
+ /*
+ * The low 12 bits of the RTE were historically the vector,
+ * delivery_mode and destination mode. Which come from the
+ * low 8 bits of the MSI data, the *next* 3 bits of the MSI
+ * data, and bit 2 of the MSI address (which thus has to be
+ * shifted up by 9 to land in the right place in bit 11 of
+ * the RTE).
+ *
+ * With Interrupt Remapping of course many bits in the MSI
+ * have different meanings but the bit-swizzling of the
+ * I/OAPIC hardware remains the same.
+ */
+ entry[0] &= 0xfffff000;
+ entry[0] |= (msg.data & (MSI_DATA_DELIVERY_MODE_MASK |
+ MSI_DATA_VECTOR_MASK));
+ entry[0] |= (msg.address_lo & MSI_ADDR_DEST_MODE_MASK) << 9;
+
+ /*
+ * Top 16 bits of the RTE are the destination and extended
+ * destination ID fields, which come from bits 19-4 of the
+ * MSI address.
+ */
+ entry[1] &= 0xffff;
+ entry[1] |= (msg.address_lo & MSI_ADDR_DEST_ID_MASK) << 12;
+}
+
+
static void ioapic_configure_entry(struct irq_data *irqd)
{
struct mp_chip_data *mpd = irqd->chip_data;
- struct irq_cfg *cfg = irqd_cfg(irqd);
struct irq_pin_list *entry;
/*
- * Only update when the parent is the vector domain, don't touch it
- * if the parent is the remapping domain. Check the installed
- * ioapic chip to verify that.
+ * The polarity, trigger and mask bits have already been
+ * set up at allocation time by mp_setup_entry(). What
+ * remains on activation and set_affinity is to set up
+ * the various destination bits which are obtained from
+ * the upstream irq domain's generated MSI message.
*/
- if (irqd->chip == &ioapic_chip) {
- mpd->entry.dest = cfg->dest_apicid & 0xff;
- mpd->entry.virt_ext_dest = cfg->dest_apicid >> 8;
- mpd->entry.vector = cfg->vector;
- }
+ mp_swizzle_msi_dest_bits(irqd, &mpd->entry);
+
for_each_irq_pin(entry, mpd->irq_2_pin)
__ioapic_write_entry(entry->apic, entry->pin, mpd->entry);
}
}
}
-static void mp_setup_entry(struct irq_cfg *cfg, struct mp_chip_data *data,
- struct IO_APIC_route_entry *entry)
+static void mp_setup_entry(struct irq_data *irq_data, struct mp_chip_data *data)
{
+ struct IO_APIC_route_entry *entry = &data->entry;
+
+ /*
+ * The destination bits get set up by ioapic_configure_entry()
+ * when the IRQ is activated. For now just set up the I/OAPIC
+ * specific fields.
+ */
memset(entry, 0, sizeof(*entry));
- entry->delivery_mode = apic->irq_delivery_mode;
- entry->dest_mode = apic->irq_dest_mode;
- entry->dest = cfg->dest_apicid & 0xff;
- entry->virt_ext_dest = cfg->dest_apicid >> 8;
- entry->vector = cfg->vector;
entry->trigger = data->trigger;
entry->polarity = data->polarity;
/*
if (!data)
return -ENOMEM;
- info->ioapic.entry = &data->entry;
ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, info);
if (ret < 0) {
kfree(data);
add_pin_to_irq_node(data, ioapic_alloc_attr_node(info), ioapic, pin);
local_irq_save(flags);
- if (info->ioapic.entry)
- mp_setup_entry(cfg, data, info->ioapic.entry);
+ mp_setup_entry(irq_data, data);
mp_register_handler(virq, data->trigger);
if (virq < nr_legacy_irqs())
legacy_pic->mask(virq);
{
struct irq_2_irte *irte_info = &data->irq_2_irte;
struct msi_msg *msg = &data->msi_entry;
- struct IO_APIC_route_entry *entry;
struct amd_iommu *iommu = amd_iommu_rlookup_table[devid];
if (!iommu)
switch (info->type) {
case X86_IRQ_ALLOC_TYPE_IOAPIC:
- /* Setup IOAPIC entry */
- entry = info->ioapic.entry;
- info->ioapic.entry = NULL;
- memset(entry, 0, sizeof(*entry));
- entry->vector = index;
- entry->mask = 0;
- entry->trigger = info->ioapic.trigger;
- entry->polarity = info->ioapic.polarity;
- /* Mask level triggered irqs. */
- if (info->ioapic.trigger)
- entry->mask = 1;
- break;
-
case X86_IRQ_ALLOC_TYPE_HPET:
case X86_IRQ_ALLOC_TYPE_PCI_MSI:
case X86_IRQ_ALLOC_TYPE_PCI_MSIX:
{
struct irq_data *parent = data->parent_data;
struct irq_cfg *cfg = irqd_cfg(data);
- struct IO_APIC_route_entry *entry;
int ret;
/* Return error If new irq affinity is out of ioapic_max_cpumask. */
if (ret < 0 || ret == IRQ_SET_MASK_OK_DONE)
return ret;
- entry = data->chip_data;
- entry->dest = cfg->dest_apicid;
- entry->vector = cfg->vector;
send_cleanup_vector(cfg);
return 0;
irq_data->chip = &hyperv_ir_chip;
- /*
- * If there is interrupt remapping function of IOMMU, setting irq
- * affinity only needs to change IRTE of IOMMU. But Hyper-V doesn't
- * support interrupt remapping function, setting irq affinity of IO-APIC
- * interrupts still needs to change IO-APIC registers. But ioapic_
- * configure_entry() will ignore value of cfg->vector and cfg->
- * dest_apicid when IO-APIC's parent irq domain is not the vector
- * domain.(See ioapic_configure_entry()) In order to setting vector
- * and dest_apicid to IO-APIC register, IO-APIC entry pointer is saved
- * in the chip_data and hyperv_irq_remapping_activate()/hyperv_ir_set_
- * affinity() set vector and dest_apicid directly into IO-APIC entry.
- */
- irq_data->chip_data = info->ioapic.entry;
-
/*
* Hypver-V IO APIC irq affinity should be in the scope of
* ioapic_max_cpumask because no irq remapping support.
irq_domain_free_irqs_common(domain, virq, nr_irqs);
}
-static int hyperv_irq_remapping_activate(struct irq_domain *domain,
- struct irq_data *irq_data, bool reserve)
-{
- struct irq_cfg *cfg = irqd_cfg(irq_data);
- struct IO_APIC_route_entry *entry = irq_data->chip_data;
-
- entry->dest = cfg->dest_apicid;
- entry->vector = cfg->vector;
-
- return 0;
-}
-
static const struct irq_domain_ops hyperv_ir_domain_ops = {
.alloc = hyperv_irq_remapping_alloc,
.free = hyperv_irq_remapping_free,
- .activate = hyperv_irq_remapping_activate,
};
static int __init hyperv_prepare_irq_remapping(void)
struct irq_alloc_info *info,
int index, int sub_handle)
{
- struct IR_IO_APIC_route_entry *entry;
struct irte *irte = &data->irte_entry;
struct msi_msg *msg = &data->msi_entry;
irte->avail, irte->vector, irte->dest_id,
irte->sid, irte->sq, irte->svt);
- entry = (struct IR_IO_APIC_route_entry *)info->ioapic.entry;
- info->ioapic.entry = NULL;
- memset(entry, 0, sizeof(*entry));
- entry->index2 = (index >> 15) & 0x1;
- entry->zero = 0;
- entry->format = 1;
- entry->index = (index & 0x7fff);
/*
* IO-APIC RTE will be configured with virtual vector.
* irq handler will do the explicit EOI to the io-apic.
*/
- entry->vector = info->ioapic.pin;
- entry->mask = 0; /* enable IRQ */
- entry->trigger = info->ioapic.trigger;
- entry->polarity = info->ioapic.polarity;
- if (info->ioapic.trigger)
- entry->mask = 1; /* Mask level triggered irqs. */
+ msg->data = info->ioapic.pin;
+ msg->address_hi = MSI_ADDR_BASE_HI;
+ msg->address_lo = MSI_ADDR_BASE_LO | MSI_ADDR_IR_EXT_INT |
+ MSI_ADDR_IR_INDEX1(index) |
+ MSI_ADDR_IR_INDEX2(index);
break;
case X86_IRQ_ALLOC_TYPE_HPET: