Skip to content

Commit 0be8153

Browse files
Marc ZyngierKAGA-KOKO
authored andcommitted
genirq/msi: Allow level-triggered MSIs to be exposed by MSI providers
So far, MSIs have been used to signal edge-triggered interrupts, as a write is a good model for an edge (you can't "unwrite" something). On the other hand, routing zillions of wires in an SoC because you need level interrupts is a bit extreme. People have come up with a variety of schemes to support this, which involves sending two messages: one to signal the interrupt, and one to clear it. Since the kernel cannot represent this, we've ended up with side-band mechanisms that are pretty awful. Instead, let's acknoledge the requirement, and ensure that, under the right circumstances, the irq_compose_msg and irq_write_msg can take as a parameter an array of two messages instead of a pointer to a single one. We also add some checking that the compose method only clobbers the second message if the MSI domain has been created with the MSI_FLAG_LEVEL_CAPABLE flags. Signed-off-by: Marc Zyngier <marc.zyngier@arm.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Cc: Rob Herring <robh@kernel.org> Cc: Jason Cooper <jason@lakedaemon.net> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> Cc: Thomas Petazzoni <thomas.petazzoni@bootlin.com> Cc: Miquel Raynal <miquel.raynal@bootlin.com> Link: https://lkml.kernel.org/r/20180508121438.11301-2-marc.zyngier@arm.com
1 parent b5c5f39 commit 0be8153

File tree

2 files changed

+26
-9
lines changed

2 files changed

+26
-9
lines changed

include/linux/msi.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,8 @@ enum {
289289
* MSI_FLAG_ACTIVATE_EARLY has been set.
290290
*/
291291
MSI_FLAG_MUST_REACTIVATE = (1 << 5),
292+
/* Is level-triggered capable, using two messages */
293+
MSI_FLAG_LEVEL_CAPABLE = (1 << 6),
292294
};
293295

294296
int msi_domain_set_affinity(struct irq_data *data, const struct cpumask *mask,

kernel/irq/msi.c

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,19 @@ static inline void irq_chip_write_msi_msg(struct irq_data *data,
7676
data->chip->irq_write_msi_msg(data, msg);
7777
}
7878

79+
static void msi_check_level(struct irq_domain *domain, struct msi_msg *msg)
80+
{
81+
struct msi_domain_info *info = domain->host_data;
82+
83+
/*
84+
* If the MSI provider has messed with the second message and
85+
* not advertized that it is level-capable, signal the breakage.
86+
*/
87+
WARN_ON(!((info->flags & MSI_FLAG_LEVEL_CAPABLE) &&
88+
(info->chip->flags & IRQCHIP_SUPPORTS_LEVEL_MSI)) &&
89+
(msg[1].address_lo || msg[1].address_hi || msg[1].data));
90+
}
91+
7992
/**
8093
* msi_domain_set_affinity - Generic affinity setter function for MSI domains
8194
* @irq_data: The irq data associated to the interrupt
@@ -89,13 +102,14 @@ int msi_domain_set_affinity(struct irq_data *irq_data,
89102
const struct cpumask *mask, bool force)
90103
{
91104
struct irq_data *parent = irq_data->parent_data;
92-
struct msi_msg msg;
105+
struct msi_msg msg[2] = { [1] = { }, };
93106
int ret;
94107

95108
ret = parent->chip->irq_set_affinity(parent, mask, force);
96109
if (ret >= 0 && ret != IRQ_SET_MASK_OK_DONE) {
97-
BUG_ON(irq_chip_compose_msi_msg(irq_data, &msg));
98-
irq_chip_write_msi_msg(irq_data, &msg);
110+
BUG_ON(irq_chip_compose_msi_msg(irq_data, msg));
111+
msi_check_level(irq_data->domain, msg);
112+
irq_chip_write_msi_msg(irq_data, msg);
99113
}
100114

101115
return ret;
@@ -104,20 +118,21 @@ int msi_domain_set_affinity(struct irq_data *irq_data,
104118
static int msi_domain_activate(struct irq_domain *domain,
105119
struct irq_data *irq_data, bool early)
106120
{
107-
struct msi_msg msg;
121+
struct msi_msg msg[2] = { [1] = { }, };
108122

109-
BUG_ON(irq_chip_compose_msi_msg(irq_data, &msg));
110-
irq_chip_write_msi_msg(irq_data, &msg);
123+
BUG_ON(irq_chip_compose_msi_msg(irq_data, msg));
124+
msi_check_level(irq_data->domain, msg);
125+
irq_chip_write_msi_msg(irq_data, msg);
111126
return 0;
112127
}
113128

114129
static void msi_domain_deactivate(struct irq_domain *domain,
115130
struct irq_data *irq_data)
116131
{
117-
struct msi_msg msg;
132+
struct msi_msg msg[2];
118133

119-
memset(&msg, 0, sizeof(msg));
120-
irq_chip_write_msi_msg(irq_data, &msg);
134+
memset(msg, 0, sizeof(msg));
135+
irq_chip_write_msi_msg(irq_data, msg);
121136
}
122137

123138
static int msi_domain_alloc(struct irq_domain *domain, unsigned int virq,

0 commit comments

Comments
 (0)