|
@@ -76,6 +76,19 @@ static inline void irq_chip_write_msi_msg(struct irq_data *data,
|
|
|
data->chip->irq_write_msi_msg(data, msg);
|
|
|
}
|
|
|
|
|
|
+static void msi_check_level(struct irq_domain *domain, struct msi_msg *msg)
|
|
|
+{
|
|
|
+ struct msi_domain_info *info = domain->host_data;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If the MSI provider has messed with the second message and
|
|
|
+ * not advertized that it is level-capable, signal the breakage.
|
|
|
+ */
|
|
|
+ WARN_ON(!((info->flags & MSI_FLAG_LEVEL_CAPABLE) &&
|
|
|
+ (info->chip->flags & IRQCHIP_SUPPORTS_LEVEL_MSI)) &&
|
|
|
+ (msg[1].address_lo || msg[1].address_hi || msg[1].data));
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* msi_domain_set_affinity - Generic affinity setter function for MSI domains
|
|
|
* @irq_data: The irq data associated to the interrupt
|
|
@@ -89,13 +102,14 @@ int msi_domain_set_affinity(struct irq_data *irq_data,
|
|
|
const struct cpumask *mask, bool force)
|
|
|
{
|
|
|
struct irq_data *parent = irq_data->parent_data;
|
|
|
- struct msi_msg msg;
|
|
|
+ struct msi_msg msg[2] = { [1] = { }, };
|
|
|
int ret;
|
|
|
|
|
|
ret = parent->chip->irq_set_affinity(parent, mask, force);
|
|
|
if (ret >= 0 && ret != IRQ_SET_MASK_OK_DONE) {
|
|
|
- BUG_ON(irq_chip_compose_msi_msg(irq_data, &msg));
|
|
|
- irq_chip_write_msi_msg(irq_data, &msg);
|
|
|
+ BUG_ON(irq_chip_compose_msi_msg(irq_data, msg));
|
|
|
+ msi_check_level(irq_data->domain, msg);
|
|
|
+ irq_chip_write_msi_msg(irq_data, msg);
|
|
|
}
|
|
|
|
|
|
return ret;
|
|
@@ -104,20 +118,21 @@ int msi_domain_set_affinity(struct irq_data *irq_data,
|
|
|
static int msi_domain_activate(struct irq_domain *domain,
|
|
|
struct irq_data *irq_data, bool early)
|
|
|
{
|
|
|
- struct msi_msg msg;
|
|
|
+ struct msi_msg msg[2] = { [1] = { }, };
|
|
|
|
|
|
- BUG_ON(irq_chip_compose_msi_msg(irq_data, &msg));
|
|
|
- irq_chip_write_msi_msg(irq_data, &msg);
|
|
|
+ BUG_ON(irq_chip_compose_msi_msg(irq_data, msg));
|
|
|
+ msi_check_level(irq_data->domain, msg);
|
|
|
+ irq_chip_write_msi_msg(irq_data, msg);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
static void msi_domain_deactivate(struct irq_domain *domain,
|
|
|
struct irq_data *irq_data)
|
|
|
{
|
|
|
- struct msi_msg msg;
|
|
|
+ struct msi_msg msg[2];
|
|
|
|
|
|
- memset(&msg, 0, sizeof(msg));
|
|
|
- irq_chip_write_msi_msg(irq_data, &msg);
|
|
|
+ memset(msg, 0, sizeof(msg));
|
|
|
+ irq_chip_write_msi_msg(irq_data, msg);
|
|
|
}
|
|
|
|
|
|
static int msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
|