Skip to content

Commit 0ee1578

Browse files
committed
genirq/msi: Provide allocation/free functions for "wired" MSI interrupts
To support wire to MSI bridges proper in the MSI core infrastructure it is required to have separate allocation/free interfaces which can be invoked from the regular irqdomain allocaton/free functions. The mechanism for allocation is: - Allocate the next free MSI descriptor index in the domain - Store the hardware interrupt number and the trigger type which was extracted by the irqdomain core from the firmware spec in the MSI descriptor device cookie so it can be retrieved by the underlying interrupt domain and interrupt chip - Use the regular MSI allocation mechanism for the newly allocated index which returns a fully initialized Linux interrupt on succes This works because: - the domains have a fixed size - each hardware interrupt is only allocated once - the underlying domain does not care about the MSI index it only cares about the hardware interrupt number and the trigger type The free function looks up the MSI index in the MSI descriptor of the provided Linux interrupt number and uses the regular index based free functions of the MSI core. Signed-off-by: Thomas Gleixner <[email protected]> Signed-off-by: Anup Patel <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 9d1c58c commit 0ee1578

File tree

2 files changed

+85
-0
lines changed

2 files changed

+85
-0
lines changed

include/linux/irqdomain.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,23 @@ static inline bool irq_domain_is_msi_device(struct irq_domain *domain)
619619

620620
#endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */
621621

622+
#ifdef CONFIG_GENERIC_MSI_IRQ
623+
int msi_device_domain_alloc_wired(struct irq_domain *domain, unsigned int hwirq,
624+
unsigned int type);
625+
void msi_device_domain_free_wired(struct irq_domain *domain, unsigned int virq);
626+
#else
627+
static inline int msi_device_domain_alloc_wired(struct irq_domain *domain, unsigned int hwirq,
628+
unsigned int type)
629+
{
630+
WARN_ON_ONCE(1);
631+
return -EINVAL;
632+
}
633+
static inline void msi_device_domain_free_wired(struct irq_domain *domain, unsigned int virq)
634+
{
635+
WARN_ON_ONCE(1);
636+
}
637+
#endif
638+
622639
#else /* CONFIG_IRQ_DOMAIN */
623640
static inline void irq_dispose_mapping(unsigned int virq) { }
624641
static inline struct irq_domain *irq_find_matching_fwnode(

kernel/irq/msi.c

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1540,6 +1540,50 @@ struct msi_map msi_domain_alloc_irq_at(struct device *dev, unsigned int domid, u
15401540
return map;
15411541
}
15421542

1543+
/**
1544+
* msi_device_domain_alloc_wired - Allocate a "wired" interrupt on @domain
1545+
* @domain: The domain to allocate on
1546+
* @hwirq: The hardware interrupt number to allocate for
1547+
* @type: The interrupt type
1548+
*
1549+
* This weirdness supports wire to MSI controllers like MBIGEN.
1550+
*
1551+
* @hwirq is the hardware interrupt number which is handed in from
1552+
* irq_create_fwspec_mapping(). As the wire to MSI domain is sparse, but
1553+
* sized in firmware, the hardware interrupt number cannot be used as MSI
1554+
* index. For the underlying irq chip the MSI index is irrelevant and
1555+
* all it needs is the hardware interrupt number.
1556+
*
1557+
* To handle this the MSI index is allocated with MSI_ANY_INDEX and the
1558+
* hardware interrupt number is stored along with the type information in
1559+
* msi_desc::cookie so the underlying interrupt chip and domain code can
1560+
* retrieve it.
1561+
*
1562+
* Return: The Linux interrupt number (> 0) or an error code
1563+
*/
1564+
int msi_device_domain_alloc_wired(struct irq_domain *domain, unsigned int hwirq,
1565+
unsigned int type)
1566+
{
1567+
unsigned int domid = MSI_DEFAULT_DOMAIN;
1568+
union msi_instance_cookie icookie = { };
1569+
struct device *dev = domain->dev;
1570+
struct msi_map map = { };
1571+
1572+
if (WARN_ON_ONCE(!dev || domain->bus_token != DOMAIN_BUS_WIRED_TO_MSI))
1573+
return -EINVAL;
1574+
1575+
icookie.value = ((u64)type << 32) | hwirq;
1576+
1577+
msi_lock_descs(dev);
1578+
if (WARN_ON_ONCE(msi_get_device_domain(dev, domid) != domain))
1579+
map.index = -EINVAL;
1580+
else
1581+
map = __msi_domain_alloc_irq_at(dev, domid, MSI_ANY_INDEX, NULL, &icookie);
1582+
msi_unlock_descs(dev);
1583+
1584+
return map.index >= 0 ? map.virq : map.index;
1585+
}
1586+
15431587
static void __msi_domain_free_irqs(struct device *dev, struct irq_domain *domain,
15441588
struct msi_ctrl *ctrl)
15451589
{
@@ -1665,6 +1709,30 @@ void msi_domain_free_irqs_all(struct device *dev, unsigned int domid)
16651709
msi_unlock_descs(dev);
16661710
}
16671711

1712+
/**
1713+
* msi_device_domain_free_wired - Free a wired interrupt in @domain
1714+
* @domain: The domain to free the interrupt on
1715+
* @virq: The Linux interrupt number to free
1716+
*
1717+
* This is the counterpart of msi_device_domain_alloc_wired() for the
1718+
* weird wired to MSI converting domains.
1719+
*/
1720+
void msi_device_domain_free_wired(struct irq_domain *domain, unsigned int virq)
1721+
{
1722+
struct msi_desc *desc = irq_get_msi_desc(virq);
1723+
struct device *dev = domain->dev;
1724+
1725+
if (WARN_ON_ONCE(!dev || !desc || domain->bus_token != DOMAIN_BUS_WIRED_TO_MSI))
1726+
return;
1727+
1728+
msi_lock_descs(dev);
1729+
if (!WARN_ON_ONCE(msi_get_device_domain(dev, MSI_DEFAULT_DOMAIN) != domain)) {
1730+
msi_domain_free_irqs_range_locked(dev, MSI_DEFAULT_DOMAIN, desc->msi_index,
1731+
desc->msi_index);
1732+
}
1733+
msi_unlock_descs(dev);
1734+
}
1735+
16681736
/**
16691737
* msi_get_domain_info - Get the MSI interrupt domain info for @domain
16701738
* @domain: The interrupt domain to retrieve data from

0 commit comments

Comments
 (0)