Commit f8264e34 authored by Jiang Liu's avatar Jiang Liu Committed by Thomas Gleixner

irqdomain: Introduce new interfaces to support hierarchy irqdomains

We plan to use hierarchy irqdomain to suppport CPU vector assignment,
interrupt remapping controller, IO-APIC controller, MSI interrupt
and hypertransport interrupt etc on x86 platforms. So extend irqdomain
interfaces to support hierarchy irqdomain.

There are already many clients of current irqdomain interfaces.
To minimize the changes, we choose to introduce new version 2 interfaces
to support hierarchy instead of extending existing irqdomain interfaces.

According to Thomas's suggestion, the most important design decision is
to build hierarchy struct irq_data to support hierarchy irqdomain, so
hierarchy irqdomain related data could be saved in struct irq_data.
With support of hierarchy irq_data, we could also support stacked
irq_chips. This is most useful in case of set_affinity().

The new hierarchy irqdomain introduces following interfaces:
1) irq_domain_alloc_irqs()/irq_domain_free_irqs(): allocate/release IRQ
   and related resources.
2) __irq_domain_alloc_irqs(): a special version to support legacy IRQs.
3) irq_domain_activate_irq()/irq_domain_deactivate_irq(): program
   interrupt controllers to activate/deactivate interrupt.

There are also several help functions to ease irqdomain implemenations:
1) irq_domain_get_irq_data(): get irq_data associated with a specific
   irqdomain.
2) irq_domain_set_hwirq_and_chip(): save irqdomain specific data into
   irq_data.
3) irq_domain_alloc_irqs_parent()/irq_domain_free_irqs_parent(): invoke
   parent irqdomain's alloc/free callbacks.

We also changed irq_startup()/irq_shutdown() to invoke
irq_domain_activate_irq()/irq_domain_deactivate_irq() to program
interrupt controller when start/stop interrupts.

[ tglx: Folded parts of the later patch series in ]
Signed-off-by: default avatarJiang Liu <jiang.liu@linux.intel.com>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Grant Likely <grant.likely@linaro.org>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Yingjoe Chen <yingjoe.chen@mediatek.com>
Cc: Yijing Wang <wangyijing@huawei.com>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent d31eb342
...@@ -151,3 +151,74 @@ used and no descriptor gets allocated it is very important to make sure ...@@ -151,3 +151,74 @@ used and no descriptor gets allocated it is very important to make sure
that the driver using the simple domain call irq_create_mapping() that the driver using the simple domain call irq_create_mapping()
before any irq_find_mapping() since the latter will actually work before any irq_find_mapping() since the latter will actually work
for the static IRQ assignment case. for the static IRQ assignment case.
==== Hierarchy IRQ domain ====
On some architectures, there may be multiple interrupt controllers
involved in delivering an interrupt from the device to the target CPU.
Let's look at a typical interrupt delivering path on x86 platforms:
Device --> IOAPIC -> Interrupt remapping Controller -> Local APIC -> CPU
There are three interrupt controllers involved:
1) IOAPIC controller
2) Interrupt remapping controller
3) Local APIC controller
To support such a hardware topology and make software architecture match
hardware architecture, an irq_domain data structure is built for each
interrupt controller and those irq_domains are organized into hierarchy.
When building irq_domain hierarchy, the irq_domain near to the device is
child and the irq_domain near to CPU is parent. So a hierarchy structure
as below will be built for the example above.
CPU Vector irq_domain (root irq_domain to manage CPU vectors)
^
|
Interrupt Remapping irq_domain (manage irq_remapping entries)
^
|
IOAPIC irq_domain (manage IOAPIC delivery entries/pins)
There are four major interfaces to use hierarchy irq_domain:
1) irq_domain_alloc_irqs(): allocate IRQ descriptors and interrupt
controller related resources to deliver these interrupts.
2) irq_domain_free_irqs(): free IRQ descriptors and interrupt controller
related resources associated with these interrupts.
3) irq_domain_activate_irq(): activate interrupt controller hardware to
deliver the interrupt.
3) irq_domain_deactivate_irq(): deactivate interrupt controller hardware
to stop delivering the interrupt.
Following changes are needed to support hierarchy irq_domain.
1) a new field 'parent' is added to struct irq_domain; it's used to
maintain irq_domain hierarchy information.
2) a new field 'parent_data' is added to struct irq_data; it's used to
build hierarchy irq_data to match hierarchy irq_domains. The irq_data
is used to store irq_domain pointer and hardware irq number.
3) new callbacks are added to struct irq_domain_ops to support hierarchy
irq_domain operations.
With support of hierarchy irq_domain and hierarchy irq_data ready, an
irq_domain structure is built for each interrupt controller, and an
irq_data structure is allocated for each irq_domain associated with an
IRQ. Now we could go one step further to support stacked(hierarchy)
irq_chip. That is, an irq_chip is associated with each irq_data along
the hierarchy. A child irq_chip may implement a required action by
itself or by cooperating with its parent irq_chip.
With stacked irq_chip, interrupt controller driver only needs to deal
with the hardware managed by itself and may ask for services from its
parent irq_chip when needed. So we could achieve a much cleaner
software architecture.
For an interrupt controller driver to support hierarchy irq_domain, it
needs to:
1) Implement irq_domain_ops.alloc and irq_domain_ops.free
2) Optionally implement irq_domain_ops.activate and
irq_domain_ops.deactivate.
3) Optionally implement an irq_chip to manage the interrupt controller
hardware.
4) No need to implement irq_domain_ops.map and irq_domain_ops.unmap,
they are unused with hierarchy irq_domain.
Hierarchy irq_domain may also be used to support other architectures,
such as ARM, ARM64 etc.
...@@ -133,6 +133,8 @@ struct irq_domain; ...@@ -133,6 +133,8 @@ struct irq_domain;
* @chip: low level interrupt hardware access * @chip: low level interrupt hardware access
* @domain: Interrupt translation domain; responsible for mapping * @domain: Interrupt translation domain; responsible for mapping
* between hwirq number and linux irq number. * between hwirq number and linux irq number.
* @parent_data: pointer to parent struct irq_data to support hierarchy
* irq_domain
* @handler_data: per-IRQ data for the irq_chip methods * @handler_data: per-IRQ data for the irq_chip methods
* @chip_data: platform-specific per-chip private data for the chip * @chip_data: platform-specific per-chip private data for the chip
* methods, to allow shared chip implementations * methods, to allow shared chip implementations
...@@ -151,6 +153,9 @@ struct irq_data { ...@@ -151,6 +153,9 @@ struct irq_data {
unsigned int state_use_accessors; unsigned int state_use_accessors;
struct irq_chip *chip; struct irq_chip *chip;
struct irq_domain *domain; struct irq_domain *domain;
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
struct irq_data *parent_data;
#endif
void *handler_data; void *handler_data;
void *chip_data; void *chip_data;
struct msi_desc *msi_desc; struct msi_desc *msi_desc;
......
...@@ -38,6 +38,8 @@ ...@@ -38,6 +38,8 @@
struct device_node; struct device_node;
struct irq_domain; struct irq_domain;
struct of_device_id; struct of_device_id;
struct irq_chip;
struct irq_data;
/* Number of irqs reserved for a legacy isa controller */ /* Number of irqs reserved for a legacy isa controller */
#define NUM_ISA_INTERRUPTS 16 #define NUM_ISA_INTERRUPTS 16
...@@ -64,6 +66,16 @@ struct irq_domain_ops { ...@@ -64,6 +66,16 @@ struct irq_domain_ops {
int (*xlate)(struct irq_domain *d, struct device_node *node, int (*xlate)(struct irq_domain *d, struct device_node *node,
const u32 *intspec, unsigned int intsize, const u32 *intspec, unsigned int intsize,
unsigned long *out_hwirq, unsigned int *out_type); unsigned long *out_hwirq, unsigned int *out_type);
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
/* extended V2 interfaces to support hierarchy irq_domains */
int (*alloc)(struct irq_domain *d, unsigned int virq,
unsigned int nr_irqs, void *arg);
void (*free)(struct irq_domain *d, unsigned int virq,
unsigned int nr_irqs);
void (*activate)(struct irq_domain *d, struct irq_data *irq_data);
void (*deactivate)(struct irq_domain *d, struct irq_data *irq_data);
#endif
}; };
extern struct irq_domain_ops irq_generic_chip_ops; extern struct irq_domain_ops irq_generic_chip_ops;
...@@ -77,6 +89,7 @@ struct irq_domain_chip_generic; ...@@ -77,6 +89,7 @@ struct irq_domain_chip_generic;
* @ops: pointer to irq_domain methods * @ops: pointer to irq_domain methods
* @host_data: private data pointer for use by owner. Not touched by irq_domain * @host_data: private data pointer for use by owner. Not touched by irq_domain
* core code. * core code.
* @flags: host per irq_domain flags
* *
* Optional elements * Optional elements
* @of_node: Pointer to device tree nodes associated with the irq_domain. Used * @of_node: Pointer to device tree nodes associated with the irq_domain. Used
...@@ -84,6 +97,7 @@ struct irq_domain_chip_generic; ...@@ -84,6 +97,7 @@ struct irq_domain_chip_generic;
* @gc: Pointer to a list of generic chips. There is a helper function for * @gc: Pointer to a list of generic chips. There is a helper function for
* setting up one or more generic chips for interrupt controllers * setting up one or more generic chips for interrupt controllers
* drivers using the generic chip library which uses this pointer. * drivers using the generic chip library which uses this pointer.
* @parent: Pointer to parent irq_domain to support hierarchy irq_domains
* *
* Revmap data, used internally by irq_domain * Revmap data, used internally by irq_domain
* @revmap_direct_max_irq: The largest hwirq that can be set for controllers that * @revmap_direct_max_irq: The largest hwirq that can be set for controllers that
...@@ -97,10 +111,14 @@ struct irq_domain { ...@@ -97,10 +111,14 @@ struct irq_domain {
const char *name; const char *name;
const struct irq_domain_ops *ops; const struct irq_domain_ops *ops;
void *host_data; void *host_data;
unsigned int flags;
/* Optional data */ /* Optional data */
struct device_node *of_node; struct device_node *of_node;
struct irq_domain_chip_generic *gc; struct irq_domain_chip_generic *gc;
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
struct irq_domain *parent;
#endif
/* reverse map data. The linear map gets appended to the irq_domain */ /* reverse map data. The linear map gets appended to the irq_domain */
irq_hw_number_t hwirq_max; irq_hw_number_t hwirq_max;
...@@ -110,6 +128,19 @@ struct irq_domain { ...@@ -110,6 +128,19 @@ struct irq_domain {
unsigned int linear_revmap[]; unsigned int linear_revmap[];
}; };
/* Irq domain flags */
enum {
/* Irq domain is hierarchical */
IRQ_DOMAIN_FLAG_HIERARCHY = (1 << 0),
/*
* Flags starting from IRQ_DOMAIN_FLAG_NONCORE are reserved
* for implementation specific purposes and ignored by the
* core code.
*/
IRQ_DOMAIN_FLAG_NONCORE = (1 << 16),
};
#ifdef CONFIG_IRQ_DOMAIN #ifdef CONFIG_IRQ_DOMAIN
struct irq_domain *__irq_domain_add(struct device_node *of_node, int size, struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
irq_hw_number_t hwirq_max, int direct_max, irq_hw_number_t hwirq_max, int direct_max,
...@@ -220,8 +251,75 @@ int irq_domain_xlate_onetwocell(struct irq_domain *d, struct device_node *ctrlr, ...@@ -220,8 +251,75 @@ int irq_domain_xlate_onetwocell(struct irq_domain *d, struct device_node *ctrlr,
const u32 *intspec, unsigned int intsize, const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_type); irq_hw_number_t *out_hwirq, unsigned int *out_type);
/* V2 interfaces to support hierarchy IRQ domains. */
extern struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain,
unsigned int virq);
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
extern int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
unsigned int nr_irqs, int node, void *arg,
bool realloc);
extern void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs);
extern void irq_domain_activate_irq(struct irq_data *irq_data);
extern void irq_domain_deactivate_irq(struct irq_data *irq_data);
static inline int irq_domain_alloc_irqs(struct irq_domain *domain,
unsigned int nr_irqs, int node, void *arg)
{
return __irq_domain_alloc_irqs(domain, -1, nr_irqs, node, arg, false);
}
extern int irq_domain_set_hwirq_and_chip(struct irq_domain *domain,
unsigned int virq,
irq_hw_number_t hwirq,
struct irq_chip *chip,
void *chip_data);
extern void irq_domain_reset_irq_data(struct irq_data *irq_data);
extern void irq_domain_free_irqs_common(struct irq_domain *domain,
unsigned int virq,
unsigned int nr_irqs);
extern void irq_domain_free_irqs_top(struct irq_domain *domain,
unsigned int virq, unsigned int nr_irqs);
static inline int irq_domain_alloc_irqs_parent(struct irq_domain *domain,
unsigned int irq_base,
unsigned int nr_irqs, void *arg)
{
if (domain->parent && domain->parent->ops->alloc)
return domain->parent->ops->alloc(domain->parent, irq_base,
nr_irqs, arg);
return -ENOSYS;
}
static inline void irq_domain_free_irqs_parent(struct irq_domain *domain,
unsigned int irq_base, unsigned int nr_irqs)
{
if (domain->parent && domain->parent->ops->free)
domain->parent->ops->free(domain->parent, irq_base, nr_irqs);
}
static inline bool irq_domain_is_hierarchy(struct irq_domain *domain)
{
return domain->flags & IRQ_DOMAIN_FLAG_HIERARCHY;
}
#else /* CONFIG_IRQ_DOMAIN_HIERARCHY */
static inline void irq_domain_activate_irq(struct irq_data *data) { }
static inline void irq_domain_deactivate_irq(struct irq_data *data) { }
static inline int irq_domain_alloc_irqs(struct irq_domain *domain,
unsigned int nr_irqs, int node, void *arg)
{
return -1;
}
static inline bool irq_domain_is_hierarchy(struct irq_domain *domain)
{
return false;
}
#endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */
#else /* CONFIG_IRQ_DOMAIN */ #else /* CONFIG_IRQ_DOMAIN */
static inline void irq_dispose_mapping(unsigned int virq) { } static inline void irq_dispose_mapping(unsigned int virq) { }
static inline void irq_domain_activate_irq(struct irq_data *data) { }
static inline void irq_domain_deactivate_irq(struct irq_data *data) { }
#endif /* !CONFIG_IRQ_DOMAIN */ #endif /* !CONFIG_IRQ_DOMAIN */
#endif /* _LINUX_IRQDOMAIN_H */ #endif /* _LINUX_IRQDOMAIN_H */
...@@ -55,6 +55,11 @@ config GENERIC_IRQ_CHIP ...@@ -55,6 +55,11 @@ config GENERIC_IRQ_CHIP
config IRQ_DOMAIN config IRQ_DOMAIN
bool bool
# Support for hierarchical irq domains
config IRQ_DOMAIN_HIERARCHY
bool
select IRQ_DOMAIN
config HANDLE_DOMAIN_IRQ config HANDLE_DOMAIN_IRQ
bool bool
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/kernel_stat.h> #include <linux/kernel_stat.h>
#include <linux/irqdomain.h>
#include <trace/events/irq.h> #include <trace/events/irq.h>
...@@ -178,6 +179,7 @@ int irq_startup(struct irq_desc *desc, bool resend) ...@@ -178,6 +179,7 @@ int irq_startup(struct irq_desc *desc, bool resend)
irq_state_clr_disabled(desc); irq_state_clr_disabled(desc);
desc->depth = 0; desc->depth = 0;
irq_domain_activate_irq(&desc->irq_data);
if (desc->irq_data.chip->irq_startup) { if (desc->irq_data.chip->irq_startup) {
ret = desc->irq_data.chip->irq_startup(&desc->irq_data); ret = desc->irq_data.chip->irq_startup(&desc->irq_data);
irq_state_clr_masked(desc); irq_state_clr_masked(desc);
...@@ -199,6 +201,7 @@ void irq_shutdown(struct irq_desc *desc) ...@@ -199,6 +201,7 @@ void irq_shutdown(struct irq_desc *desc)
desc->irq_data.chip->irq_disable(&desc->irq_data); desc->irq_data.chip->irq_disable(&desc->irq_data);
else else
desc->irq_data.chip->irq_mask(&desc->irq_data); desc->irq_data.chip->irq_mask(&desc->irq_data);
irq_domain_deactivate_irq(&desc->irq_data);
irq_state_set_masked(desc); irq_state_set_masked(desc);
} }
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment