Commit c207f3a4 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'irqdomain-for-linus' of git://git.secretlab.ca/git/linux-2.6

Pull irq_domain support for all architectures from Grant Likely:
 "Generialize powerpc's irq_host as irq_domain

  This branch takes the PowerPC irq_host infrastructure (reverse mapping
  from Linux IRQ numbers to hardware irq numbering), generalizes it,
  renames it to irq_domain, and makes it available to all architectures.

  Originally the plan has been to create an all-new irq_domain
  implementation which addresses some of the powerpc shortcomings such
  as not handling 1:1 mappings well, but doing that proved to be far
  more difficult and invasive than generalizing the working code and
  refactoring it in-place.  So, this branch rips out the 'new'
  irq_domain and replaces it with the modified powerpc version (in a
  fully bisectable way of course).  It converts all users over to the
  new API and makes irq_domain selectable on any architecture.

  No architecture is forced to enable irq_domain, but the infrastructure
  is required for doing OpenFirmware style irq translations.  It will
  even work on SPARC even though SPARC has it's own mechanism for
  translating irqs at boot time.  MIPS, microblaze, embedded x86 and c6x
  are converted too.

  The resulting irq_domain code is probably still too verbose and can be
  optimized more, but that can be done incrementally and is a task for
  follow-on patches."

* tag 'irqdomain-for-linus' of git://git.secretlab.ca/git/linux-2.6: (31 commits)
  dt: fix twl4030 for non-dt compile on x86
  mfd: twl-core: Add IRQ_DOMAIN dependency
  devicetree: Add empty of_platform_populate() for !CONFIG_OF_ADDRESS (sparc)
  irq_domain: Centralize definition of irq_dispose_mapping()
  irq_domain/mips: Allow irq_domain on MIPS
  irq_domain/x86: Convert x86 (embedded) to use common irq_domain
  ppc-6xx: fix build failure in flipper-pic.c and hlwd-pic.c
  irq_domain/microblaze: Convert microblaze to use irq_domains
  irq_domain/powerpc: Replace custom xlate functions with library functions
  irq_domain/powerpc: constify irq_domain_ops
  irq_domain/c6x: Use library of xlate functions
  irq_domain/c6x: constify irq_domain structures
  irq_domain/c6x: Convert c6x to use generic irq_domain support.
  irq_domain: constify irq_domain_ops
  irq_domain: Create common xlate functions that device drivers can use
  irq_domain: Remove irq_domain_add_simple()
  irq_domain: Remove 'new' irq_domain in favour of the ppc one
  mfd: twl-core.c: Fix the number of interrupts managed by twl4030
  of/address: add empty static inlines for !CONFIG_OF
  irq_domain: Add support for base irq and hwirq in legacy mappings
  ...
parents c7c66c0c e7cc3aca
irq_domain interrupt number mapping library
The current design of the Linux kernel uses a single large number
space where each separate IRQ source is assigned a different number.
This is simple when there is only one interrupt controller, but in
systems with multiple interrupt controllers the kernel must ensure
that each one gets assigned non-overlapping allocations of Linux
IRQ numbers.
The irq_alloc_desc*() and irq_free_desc*() APIs provide allocation of
irq numbers, but they don't provide any support for reverse mapping of
the controller-local IRQ (hwirq) number into the Linux IRQ number
space.
The irq_domain library adds mapping between hwirq and IRQ numbers on
top of the irq_alloc_desc*() API. An irq_domain to manage mapping is
preferred over interrupt controller drivers open coding their own
reverse mapping scheme.
irq_domain also implements translation from Device Tree interrupt
specifiers to hwirq numbers, and can be easily extended to support
other IRQ topology data sources.
=== irq_domain usage ===
An interrupt controller driver creates and registers an irq_domain by
calling one of the irq_domain_add_*() functions (each mapping method
has a different allocator function, more on that later). The function
will return a pointer to the irq_domain on success. The caller must
provide the allocator function with an irq_domain_ops structure with
the .map callback populated as a minimum.
In most cases, the irq_domain will begin empty without any mappings
between hwirq and IRQ numbers. Mappings are added to the irq_domain
by calling irq_create_mapping() which accepts the irq_domain and a
hwirq number as arguments. If a mapping for the hwirq doesn't already
exist then it will allocate a new Linux irq_desc, associate it with
the hwirq, and call the .map() callback so the driver can perform any
required hardware setup.
When an interrupt is received, irq_find_mapping() function should
be used to find the Linux IRQ number from the hwirq number.
If the driver has the Linux IRQ number or the irq_data pointer, and
needs to know the associated hwirq number (such as in the irq_chip
callbacks) then it can be directly obtained from irq_data->hwirq.
=== Types of irq_domain mappings ===
There are several mechanisms available for reverse mapping from hwirq
to Linux irq, and each mechanism uses a different allocation function.
Which reverse map type should be used depends on the use case. Each
of the reverse map types are described below:
==== Linear ====
irq_domain_add_linear()
The linear reverse map maintains a fixed size table indexed by the
hwirq number. When a hwirq is mapped, an irq_desc is allocated for
the hwirq, and the IRQ number is stored in the table.
The Linear map is a good choice when the maximum number of hwirqs is
fixed and a relatively small number (~ < 256). The advantages of this
map are fixed time lookup for IRQ numbers, and irq_descs are only
allocated for in-use IRQs. The disadvantage is that the table must be
as large as the largest possible hwirq number.
The majority of drivers should use the linear map.
==== Tree ====
irq_domain_add_tree()
The irq_domain maintains a radix tree map from hwirq numbers to Linux
IRQs. When an hwirq is mapped, an irq_desc is allocated and the
hwirq is used as the lookup key for the radix tree.
The tree map is a good choice if the hwirq number can be very large
since it doesn't need to allocate a table as large as the largest
hwirq number. The disadvantage is that hwirq to IRQ number lookup is
dependent on how many entries are in the table.
Very few drivers should need this mapping. At the moment, powerpc
iseries is the only user.
==== No Map ===-
irq_domain_add_nomap()
The No Map mapping is to be used when the hwirq number is
programmable in the hardware. In this case it is best to program the
Linux IRQ number into the hardware itself so that no mapping is
required. Calling irq_create_direct_mapping() will allocate a Linux
IRQ number and call the .map() callback so that driver can program the
Linux IRQ number into the hardware.
Most drivers cannot use this mapping.
==== Legacy ====
irq_domain_add_legacy()
irq_domain_add_legacy_isa()
The Legacy mapping is a special case for drivers that already have a
range of irq_descs allocated for the hwirqs. It is used when the
driver cannot be immediately converted to use the linear mapping. For
example, many embedded system board support files use a set of #defines
for IRQ numbers that are passed to struct device registrations. In that
case the Linux IRQ numbers cannot be dynamically assigned and the legacy
mapping should be used.
The legacy map assumes a contiguous range of IRQ numbers has already
been allocated for the controller and that the IRQ number can be
calculated by adding a fixed offset to the hwirq number, and
visa-versa. The disadvantage is that it requires the interrupt
controller to manage IRQ allocations and it requires an irq_desc to be
allocated for every hwirq, even if it is unused.
The legacy map should only be used if fixed IRQ mappings must be
supported. For example, ISA controllers would use the legacy map for
mapping Linux IRQs 0-15 so that existing ISA drivers get the correct IRQ
numbers.
......@@ -3666,6 +3666,15 @@ S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git irq/core
F: kernel/irq/
IRQ DOMAINS (IRQ NUMBER MAPPING LIBRARY)
M: Benjamin Herrenschmidt <benh@kernel.crashing.org>
M: Grant Likely <grant.likely@secretlab.ca>
T: git git://git.secretlab.ca/git/linux-2.6.git irqdomain/next
S: Maintained
F: Documentation/IRQ-domain.txt
F: include/linux/irqdomain.h
F: kernel/irq/irqdomain.c
ISAPNP
M: Jaroslav Kysela <perex@perex.cz>
S: Maintained
......
......@@ -51,7 +51,6 @@ union gic_base {
};
struct gic_chip_data {
unsigned int irq_offset;
union gic_base dist_base;
union gic_base cpu_base;
#ifdef CONFIG_CPU_PM
......@@ -61,9 +60,7 @@ struct gic_chip_data {
u32 __percpu *saved_ppi_enable;
u32 __percpu *saved_ppi_conf;
#endif
#ifdef CONFIG_IRQ_DOMAIN
struct irq_domain domain;
#endif
struct irq_domain *domain;
unsigned int gic_irqs;
#ifdef CONFIG_GIC_NON_BANKED
void __iomem *(*get_base)(union gic_base *);
......@@ -282,7 +279,7 @@ asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
irqnr = irqstat & ~0x1c00;
if (likely(irqnr > 15 && irqnr < 1021)) {
irqnr = irq_domain_to_irq(&gic->domain, irqnr);
irqnr = irq_find_mapping(gic->domain, irqnr);
handle_IRQ(irqnr, regs);
continue;
}
......@@ -314,8 +311,8 @@ static void gic_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)
if (gic_irq == 1023)
goto out;
cascade_irq = irq_domain_to_irq(&chip_data->domain, gic_irq);
if (unlikely(gic_irq < 32 || gic_irq > 1020 || cascade_irq >= NR_IRQS))
cascade_irq = irq_find_mapping(chip_data->domain, gic_irq);
if (unlikely(gic_irq < 32 || gic_irq > 1020))
do_bad_IRQ(cascade_irq, desc);
else
generic_handle_irq(cascade_irq);
......@@ -348,10 +345,9 @@ void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq)
static void __init gic_dist_init(struct gic_chip_data *gic)
{
unsigned int i, irq;
unsigned int i;
u32 cpumask;
unsigned int gic_irqs = gic->gic_irqs;
struct irq_domain *domain = &gic->domain;
void __iomem *base = gic_data_dist_base(gic);
u32 cpu = cpu_logical_map(smp_processor_id());
......@@ -386,23 +382,6 @@ static void __init gic_dist_init(struct gic_chip_data *gic)
for (i = 32; i < gic_irqs; i += 32)
writel_relaxed(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4 / 32);
/*
* Setup the Linux IRQ subsystem.
*/
irq_domain_for_each_irq(domain, i, irq) {
if (i < 32) {
irq_set_percpu_devid(irq);
irq_set_chip_and_handler(irq, &gic_chip,
handle_percpu_devid_irq);
set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
} else {
irq_set_chip_and_handler(irq, &gic_chip,
handle_fasteoi_irq);
set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
}
irq_set_chip_data(irq, gic);
}
writel_relaxed(1, base + GIC_DIST_CTRL);
}
......@@ -618,8 +597,24 @@ static void __init gic_pm_init(struct gic_chip_data *gic)
}
#endif
#ifdef CONFIG_OF
static int gic_irq_domain_dt_translate(struct irq_domain *d,
static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hw)
{
if (hw < 32) {
irq_set_percpu_devid(irq);
irq_set_chip_and_handler(irq, &gic_chip,
handle_percpu_devid_irq);
set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
} else {
irq_set_chip_and_handler(irq, &gic_chip,
handle_fasteoi_irq);
set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
}
irq_set_chip_data(irq, d->host_data);
return 0;
}
static int gic_irq_domain_xlate(struct irq_domain *d,
struct device_node *controller,
const u32 *intspec, unsigned int intsize,
unsigned long *out_hwirq, unsigned int *out_type)
......@@ -639,26 +634,23 @@ static int gic_irq_domain_dt_translate(struct irq_domain *d,
*out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
return 0;
}
#endif
const struct irq_domain_ops gic_irq_domain_ops = {
#ifdef CONFIG_OF
.dt_translate = gic_irq_domain_dt_translate,
#endif
.map = gic_irq_domain_map,
.xlate = gic_irq_domain_xlate,
};
void __init gic_init_bases(unsigned int gic_nr, int irq_start,
void __iomem *dist_base, void __iomem *cpu_base,
u32 percpu_offset)
u32 percpu_offset, struct device_node *node)
{
irq_hw_number_t hwirq_base;
struct gic_chip_data *gic;
struct irq_domain *domain;
int gic_irqs;
int gic_irqs, irq_base;
BUG_ON(gic_nr >= MAX_GIC_NR);
gic = &gic_data[gic_nr];
domain = &gic->domain;
#ifdef CONFIG_GIC_NON_BANKED
if (percpu_offset) { /* Frankein-GIC without banked registers... */
unsigned int cpu;
......@@ -694,10 +686,10 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
* For primary GICs, skip over SGIs.
* For secondary GICs, skip over PPIs, too.
*/
domain->hwirq_base = 32;
hwirq_base = 32;
if (gic_nr == 0) {
if ((irq_start & 31) > 0) {
domain->hwirq_base = 16;
hwirq_base = 16;
if (irq_start != -1)
irq_start = (irq_start & ~31) + 16;
}
......@@ -713,17 +705,17 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
gic_irqs = 1020;
gic->gic_irqs = gic_irqs;
domain->nr_irq = gic_irqs - domain->hwirq_base;
domain->irq_base = irq_alloc_descs(irq_start, 16, domain->nr_irq,
numa_node_id());
if (IS_ERR_VALUE(domain->irq_base)) {
gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */
irq_base = irq_alloc_descs(irq_start, 16, gic_irqs, numa_node_id());
if (IS_ERR_VALUE(irq_base)) {
WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
irq_start);
domain->irq_base = irq_start;
irq_base = irq_start;
}
domain->priv = gic;
domain->ops = &gic_irq_domain_ops;
irq_domain_add(domain);
gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base,
hwirq_base, &gic_irq_domain_ops, gic);
if (WARN_ON(!gic->domain))
return;
gic_chip.flags |= gic_arch_extn.flags;
gic_dist_init(gic);
......@@ -768,7 +760,6 @@ int __init gic_of_init(struct device_node *node, struct device_node *parent)
void __iomem *dist_base;
u32 percpu_offset;
int irq;
struct irq_domain *domain = &gic_data[gic_cnt].domain;
if (WARN_ON(!node))
return -ENODEV;
......@@ -782,9 +773,7 @@ int __init gic_of_init(struct device_node *node, struct device_node *parent)
if (of_property_read_u32(node, "cpu-offset", &percpu_offset))
percpu_offset = 0;
domain->of_node = of_node_get(node);
gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset);
gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset, node);
if (parent) {
irq = irq_of_parse_and_map(node, 0);
......
......@@ -56,7 +56,7 @@ struct vic_device {
u32 int_enable;
u32 soft_int;
u32 protect;
struct irq_domain domain;
struct irq_domain *domain;
};
/* we cannot allocate memory when VICs are initially registered */
......@@ -192,14 +192,8 @@ static void __init vic_register(void __iomem *base, unsigned int irq,
v->resume_sources = resume_sources;
v->irq = irq;
vic_id++;
v->domain.irq_base = irq;
v->domain.nr_irq = 32;
#ifdef CONFIG_OF_IRQ
v->domain.of_node = of_node_get(node);
#endif /* CONFIG_OF */
v->domain.ops = &irq_domain_simple_ops;
irq_domain_add(&v->domain);
v->domain = irq_domain_add_legacy(node, 32, irq, 0,
&irq_domain_simple_ops, v);
}
static void vic_ack_irq(struct irq_data *d)
......@@ -348,7 +342,7 @@ static void __init vic_init_st(void __iomem *base, unsigned int irq_start,
vic_register(base, irq_start, 0, node);
}
static void __init __vic_init(void __iomem *base, unsigned int irq_start,
void __init __vic_init(void __iomem *base, unsigned int irq_start,
u32 vic_sources, u32 resume_sources,
struct device_node *node)
{
......@@ -444,7 +438,7 @@ static int handle_one_vic(struct vic_device *vic, struct pt_regs *regs)
stat = readl_relaxed(vic->base + VIC_IRQ_STATUS);
while (stat) {
irq = ffs(stat) - 1;
handle_IRQ(irq_domain_to_irq(&vic->domain, irq), regs);
handle_IRQ(irq_find_mapping(vic->domain, irq), regs);
stat &= ~(1 << irq);
handled = 1;
}
......
......@@ -39,7 +39,7 @@ struct device_node;
extern struct irq_chip gic_arch_extn;
void gic_init_bases(unsigned int, int, void __iomem *, void __iomem *,
u32 offset);
u32 offset, struct device_node *);
int gic_of_init(struct device_node *node, struct device_node *parent);
void gic_secondary_init(unsigned int);
void gic_handle_irq(struct pt_regs *regs);
......@@ -49,7 +49,7 @@ void gic_raise_softirq(const struct cpumask *mask, unsigned int irq);
static inline void gic_init(unsigned int nr, int start,
void __iomem *dist , void __iomem *cpu)
{
gic_init_bases(nr, start, dist, cpu, 0);
gic_init_bases(nr, start, dist, cpu, 0, NULL);
}
#endif
......
......@@ -47,6 +47,8 @@
struct device_node;
struct pt_regs;
void __vic_init(void __iomem *base, unsigned int irq_start, u32 vic_sources,
u32 resume_sources, struct device_node *node);
void vic_init(void __iomem *base, unsigned int irq_start, u32 vic_sources, u32 resume_sources);
int vic_of_init(struct device_node *node, struct device_node *parent);
void vic_handle_irq(struct pt_regs *regs);
......
......@@ -402,7 +402,7 @@ void __init exynos4_init_irq(void)
gic_bank_offset = soc_is_exynos4412() ? 0x4000 : 0x8000;
if (!of_have_populated_dt())
gic_init_bases(0, IRQ_PPI(0), S5P_VA_GIC_DIST, S5P_VA_GIC_CPU, gic_bank_offset);
gic_init_bases(0, IRQ_PPI(0), S5P_VA_GIC_DIST, S5P_VA_GIC_CPU, gic_bank_offset, NULL);
#ifdef CONFIG_OF
else
of_irq_init(exynos4_dt_irq_match);
......
......@@ -47,7 +47,7 @@ static const struct of_dev_auxdata imx51_auxdata_lookup[] __initconst = {
static int __init imx51_tzic_add_irq_domain(struct device_node *np,
struct device_node *interrupt_parent)
{
irq_domain_add_simple(np, 0);
irq_domain_add_legacy(np, 128, 0, 0, &irq_domain_simple_ops, NULL);
return 0;
}
......@@ -57,7 +57,7 @@ static int __init imx51_gpio_add_irq_domain(struct device_node *np,
static int gpio_irq_base = MXC_GPIO_IRQ_START + ARCH_NR_GPIOS;
gpio_irq_base -= 32;
irq_domain_add_simple(np, gpio_irq_base);
irq_domain_add_legacy(np, 32, gpio_irq_base, 0, &irq_domain_simple_ops, NULL);
return 0;
}
......
......@@ -51,7 +51,7 @@ static const struct of_dev_auxdata imx53_auxdata_lookup[] __initconst = {
static int __init imx53_tzic_add_irq_domain(struct device_node *np,
struct device_node *interrupt_parent)
{
irq_domain_add_simple(np, 0);
irq_domain_add_legacy(np, 128, 0, 0, &irq_domain_simple_ops, NULL);
return 0;
}
......@@ -61,7 +61,7 @@ static int __init imx53_gpio_add_irq_domain(struct device_node *np,
static int gpio_irq_base = MXC_GPIO_IRQ_START + ARCH_NR_GPIOS;
gpio_irq_base -= 32;
irq_domain_add_simple(np, gpio_irq_base);
irq_domain_add_legacy(np, 32, gpio_irq_base, 0, &irq_domain_simple_ops, NULL);
return 0;
}
......
......@@ -97,7 +97,8 @@ static int __init imx6q_gpio_add_irq_domain(struct device_node *np,
static int gpio_irq_base = MXC_GPIO_IRQ_START + ARCH_NR_GPIOS;
gpio_irq_base -= 32;
irq_domain_add_simple(np, gpio_irq_base);
irq_domain_add_legacy(np, 32, gpio_irq_base, 0, &irq_domain_simple_ops,
NULL);
return 0;
}
......
......@@ -80,12 +80,8 @@ static struct of_device_id msm_dt_gic_match[] __initdata = {
static void __init msm8x60_dt_init(void)
{
struct device_node *node;
node = of_find_matching_node_by_address(NULL, msm_dt_gic_match,
MSM8X60_QGIC_DIST_PHYS);
if (node)
irq_domain_add_simple(node, GIC_SPI_START);
irq_domain_generate_simple(msm_dt_gic_match, MSM8X60_QGIC_DIST_PHYS,
GIC_SPI_START);
if (of_machine_is_compatible("qcom,msm8660-surf")) {
printk(KERN_INFO "Init surf UART registers\n");
......
......@@ -68,7 +68,7 @@ static void __init omap_generic_init(void)
{
struct device_node *node = of_find_matching_node(NULL, intc_match);
if (node)
irq_domain_add_simple(node, 0);
irq_domain_add_legacy(node, 32, 0, 0, &irq_domain_simple_ops, NULL);
omap_sdrc_init(NULL, NULL);
......
......@@ -68,7 +68,7 @@ void __init sirfsoc_of_irq_init(void)
if (!sirfsoc_intc_base)
panic("unable to map intc cpu registers\n");
irq_domain_add_simple(np, 0);
irq_domain_add_legacy(np, 32, 0, 0, &irq_domain_simple_ops, NULL);
of_node_put(np);
......
......@@ -98,8 +98,11 @@ static const struct of_device_id sic_of_match[] __initconst = {
void __init versatile_init_irq(void)
{
vic_init(VA_VIC_BASE, IRQ_VIC_START, ~0, 0);
irq_domain_generate_simple(vic_of_match, VERSATILE_VIC_BASE, IRQ_VIC_START);
struct device_node *np;
np = of_find_matching_node_by_address(NULL, vic_of_match,
VERSATILE_VIC_BASE);
__vic_init(VA_VIC_BASE, IRQ_VIC_START, ~0, 0, np);
writel(~0, VA_SIC_BASE + SIC_IRQ_ENABLE_CLEAR);
......
......@@ -12,6 +12,7 @@ config TMS320C6X
select HAVE_GENERIC_HARDIRQS
select HAVE_MEMBLOCK
select HAVE_SPARSE_IRQ
select IRQ_DOMAIN
select OF
select OF_EARLY_FLATTREE
......
......@@ -13,6 +13,7 @@
#ifndef _ASM_C6X_IRQ_H
#define _ASM_C6X_IRQ_H
#include <linux/irqdomain.h>
#include <linux/threads.h>
#include <linux/list.h>
#include <linux/radix-tree.h>
......@@ -41,253 +42,9 @@
/* This number is used when no interrupt has been assigned */
#define NO_IRQ 0
/* This type is the placeholder for a hardware interrupt number. It has to
* be big enough to enclose whatever representation is used by a given
* platform.
*/
typedef unsigned long irq_hw_number_t;
/* Interrupt controller "host" data structure. This could be defined as a
* irq domain controller. That is, it handles the mapping between hardware
* and virtual interrupt numbers for a given interrupt domain. The host
* structure is generally created by the PIC code for a given PIC instance
* (though a host can cover more than one PIC if they have a flat number
* model). It's the host callbacks that are responsible for setting the
* irq_chip on a given irq_desc after it's been mapped.
*
* The host code and data structures are fairly agnostic to the fact that
* we use an open firmware device-tree. We do have references to struct
* device_node in two places: in irq_find_host() to find the host matching
* a given interrupt controller node, and of course as an argument to its
* counterpart host->ops->match() callback. However, those are treated as
* generic pointers by the core and the fact that it's actually a device-node
* pointer is purely a convention between callers and implementation. This
* code could thus be used on other architectures by replacing those two
* by some sort of arch-specific void * "token" used to identify interrupt
* controllers.
*/
struct irq_host;
struct radix_tree_root;
struct device_node;
/* Functions below are provided by the host and called whenever a new mapping
* is created or an old mapping is disposed. The host can then proceed to
* whatever internal data structures management is required. It also needs
* to setup the irq_desc when returning from map().
*/
struct irq_host_ops {
/* Match an interrupt controller device node to a host, returns
* 1 on a match
*/
int (*match)(struct irq_host *h, struct device_node *node);
/* Create or update a mapping between a virtual irq number and a hw
* irq number. This is called only once for a given mapping.
*/
int (*map)(struct irq_host *h, unsigned int virq, irq_hw_number_t hw);
/* Dispose of such a mapping */
void (*unmap)(struct irq_host *h, unsigned int virq);
/* Translate device-tree interrupt specifier from raw format coming
* from the firmware to a irq_hw_number_t (interrupt line number) and
* type (sense) that can be passed to set_irq_type(). In the absence
* of this callback, irq_create_of_mapping() and irq_of_parse_and_map()
* will return the hw number in the first cell and IRQ_TYPE_NONE for
* the type (which amount to keeping whatever default value the
* interrupt controller has for that line)
*/
int (*xlate)(struct irq_host *h, struct device_node *ctrler,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_type);
};
struct irq_host {
struct list_head link;
/* type of reverse mapping technique */
unsigned int revmap_type;
#define IRQ_HOST_MAP_PRIORITY 0 /* core priority irqs, get irqs 1..15 */
#define IRQ_HOST_MAP_NOMAP 1 /* no fast reverse mapping */
#define IRQ_HOST_MAP_LINEAR 2 /* linear map of interrupts */
#define IRQ_HOST_MAP_TREE 3 /* radix tree */
union {
struct {
unsigned int size;
unsigned int *revmap;
} linear;
struct radix_tree_root tree;
} revmap_data;
struct irq_host_ops *ops;
void *host_data;
irq_hw_number_t inval_irq;
/* Optional device node pointer */
struct device_node *of_node;
};
struct irq_data;
extern irq_hw_number_t irqd_to_hwirq(struct irq_data *d);
extern irq_hw_number_t virq_to_hw(unsigned int virq);
extern bool virq_is_host(unsigned int virq, struct irq_host *host);
/**
* irq_alloc_host - Allocate a new irq_host data structure
* @of_node: optional device-tree node of the interrupt controller
* @revmap_type: type of reverse mapping to use
* @revmap_arg: for IRQ_HOST_MAP_LINEAR linear only: size of the map
* @ops: map/unmap host callbacks
* @inval_irq: provide a hw number in that host space that is always invalid
*
* Allocates and initialize and irq_host structure. Note that in the case of
* IRQ_HOST_MAP_LEGACY, the map() callback will be called before this returns
* for all legacy interrupts except 0 (which is always the invalid irq for
* a legacy controller). For a IRQ_HOST_MAP_LINEAR, the map is allocated by
* this call as well. For a IRQ_HOST_MAP_TREE, the radix tree will be allocated
* later during boot automatically (the reverse mapping will use the slow path
* until that happens).
*/
extern struct irq_host *irq_alloc_host(struct device_node *of_node,
unsigned int revmap_type,
unsigned int revmap_arg,
struct irq_host_ops *ops,
irq_hw_number_t inval_irq);
/**
* irq_find_host - Locates a host for a given device node
* @node: device-tree node of the interrupt controller
*/
extern struct irq_host *irq_find_host(struct device_node *node);
/**
* irq_set_default_host - Set a "default" host
* @host: default host pointer
*
* For convenience, it's possible to set a "default" host that will be used
* whenever NULL is passed to irq_create_mapping(). It makes life easier for
* platforms that want to manipulate a few hard coded interrupt numbers that
* aren't properly represented in the device-tree.
*/
extern void irq_set_default_host(struct irq_host *host);
/**
* irq_set_virq_count - Set the maximum number of virt irqs
* @count: number of linux virtual irqs, capped with NR_IRQS
*
* This is mainly for use by platforms like iSeries who want to program
* the virtual irq number in the controller to avoid the reverse mapping
*/
extern void irq_set_virq_count(unsigned int count);
/**
* irq_create_mapping - Map a hardware interrupt into linux virq space
* @host: host owning this hardware interrupt or NULL for default host
* @hwirq: hardware irq number in that host space
*
* Only one mapping per hardware interrupt is permitted. Returns a linux
* virq number.
* If the sense/trigger is to be specified, set_irq_type() should be called
* on the number returned from that call.
*/
extern unsigned int irq_create_mapping(struct irq_host *host,
irq_hw_number_t hwirq);
/**
* irq_dispose_mapping - Unmap an interrupt
* @virq: linux virq number of the interrupt to unmap
*/
extern void irq_dispose_mapping(unsigned int virq);
/**
* irq_find_mapping - Find a linux virq from an hw irq number.
* @host: host owning this hardware interrupt
* @hwirq: hardware irq number in that host space
*
* This is a slow path, for use by generic code. It's expected that an
* irq controller implementation directly calls the appropriate low level
* mapping function.
*/
extern unsigned int irq_find_mapping(struct irq_host *host,
irq_hw_number_t hwirq);
/**
* irq_create_direct_mapping - Allocate a virq for direct mapping
* @host: host to allocate the virq for or NULL for default host
*
* This routine is used for irq controllers which can choose the hardware
* interrupt numbers they generate. In such a case it's simplest to use
* the linux virq as the hardware interrupt number.
*/
extern unsigned int irq_create_direct_mapping(struct irq_host *host);
/**
* irq_radix_revmap_insert - Insert a hw irq to linux virq number mapping.
* @host: host owning this hardware interrupt
* @virq: linux irq number
* @hwirq: hardware irq number in that host space
*
* This is for use by irq controllers that use a radix tree reverse
* mapping for fast lookup.
*/
extern void irq_radix_revmap_insert(struct irq_host *host, unsigned int virq,
irq_hw_number_t hwirq);
/**
* irq_radix_revmap_lookup - Find a linux virq from a hw irq number.
* @host: host owning this hardware interrupt
* @hwirq: hardware irq number in that host space
*
* This is a fast path, for use by irq controller code that uses radix tree
* revmaps
*/
extern unsigned int irq_radix_revmap_lookup(struct irq_host *host,
irq_hw_number_t hwirq);
/**
* irq_linear_revmap - Find a linux virq from a hw irq number.
* @host: host owning this hardware interrupt
* @hwirq: hardware irq number in that host space
*
* This is a fast path, for use by irq controller code that uses linear
* revmaps. It does fallback to the slow path if the revmap doesn't exist
* yet and will create the revmap entry with appropriate locking
*/
extern unsigned int irq_linear_revmap(struct irq_host *host,
irq_hw_number_t hwirq);
/**
* irq_alloc_virt - Allocate virtual irq numbers
* @host: host owning these new virtual irqs
* @count: number of consecutive numbers to allocate
* @hint: pass a hint number, the allocator will try to use a 1:1 mapping
*
* This is a low level function that is used internally by irq_create_mapping()
* and that can be used by some irq controllers implementations for things
* like allocating ranges of numbers for MSIs. The revmaps are left untouched.
*/
extern unsigned int irq_alloc_virt(struct irq_host *host,
unsigned int count,
unsigned int hint);
/**
* irq_free_virt - Free virtual irq numbers
* @virq: virtual irq number of the first interrupt to free
* @count: number of interrupts to free
*
* This function is the opposite of irq_alloc_virt. It will not clear reverse
* maps, this should be done previously by unmap'ing the interrupt. In fact,
* all interrupts covered by the range being freed should have been unmapped
* prior to calling this.
*/
extern void irq_free_virt(unsigned int virq, unsigned int count);
extern void __init init_pic_c64xplus(void);
......
......@@ -73,9 +73,9 @@ asmlinkage void c6x_do_IRQ(unsigned int prio, struct pt_regs *regs)
set_irq_regs(old_regs);
}
static struct irq_host *core_host;
static struct irq_domain *core_domain;
static int core_host_map(struct irq_host *h, unsigned int virq,
static int core_domain_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
if (hw < 4 || hw >= NR_PRIORITY_IRQS)
......@@ -86,8 +86,9 @@ static int core_host_map(struct irq_host *h, unsigned int virq,
return 0;
}
static struct irq_host_ops core_host_ops = {
.map = core_host_map,
static const struct irq_domain_ops core_domain_ops = {
.map = core_domain_map,
.xlate = irq_domain_xlate_onecell,
};
void __init init_IRQ(void)
......@@ -100,10 +101,11 @@ void __init init_IRQ(void)
np = of_find_compatible_node(NULL, NULL, "ti,c64x+core-pic");
if (np != NULL) {
/* create the core host */
core_host = irq_alloc_host(np, IRQ_HOST_MAP_PRIORITY, 0,
&core_host_ops, 0);
if (core_host)
irq_set_default_host(core_host);
core_domain = irq_domain_add_legacy(np, NR_PRIORITY_IRQS,
0, 0, &core_domain_ops,
NULL);
if (core_domain)
irq_set_default_host(core_domain);
of_node_put(np);
}
......@@ -128,601 +130,15 @@ int arch_show_interrupts(struct seq_file *p, int prec)
return 0;
}
/*
* IRQ controller and virtual interrupts
*/
/* The main irq map itself is an array of NR_IRQ entries containing the
* associate host and irq number. An entry with a host of NULL is free.
* An entry can be allocated if it's free, the allocator always then sets
* hwirq first to the host's invalid irq number and then fills ops.
*/
struct irq_map_entry {
irq_hw_number_t hwirq;
struct irq_host *host;
};
static LIST_HEAD(irq_hosts);
static DEFINE_RAW_SPINLOCK(irq_big_lock);
static DEFINE_MUTEX(revmap_trees_mutex);
static struct irq_map_entry irq_map[NR_IRQS];
static unsigned int irq_virq_count = NR_IRQS;
static struct irq_host *irq_default_host;
irq_hw_number_t irqd_to_hwirq(struct irq_data *d)
{
return irq_map[d->irq].hwirq;
return d->hwirq;
}
EXPORT_SYMBOL_GPL(irqd_to_hwirq);
irq_hw_number_t virq_to_hw(unsigned int virq)
{
return irq_map[virq].hwirq;
struct irq_data *irq_data = irq_get_irq_data(virq);
return WARN_ON(!irq_data) ? 0 : irq_data->hwirq;
}
EXPORT_SYMBOL_GPL(virq_to_hw);
bool virq_is_host(unsigned int virq, struct irq_host *host)
{
return irq_map[virq].host == host;
}
EXPORT_SYMBOL_GPL(virq_is_host);
static int default_irq_host_match(struct irq_host *h, struct device_node *np)
{
return h->of_node != NULL && h->of_node == np;
}
struct irq_host *irq_alloc_host(struct device_node *of_node,
unsigned int revmap_type,
unsigned int revmap_arg,
struct irq_host_ops *ops,
irq_hw_number_t inval_irq)
{
struct irq_host *host;
unsigned int size = sizeof(struct irq_host);
unsigned int i;
unsigned int *rmap;
unsigned long flags;
/* Allocate structure and revmap table if using linear mapping */
if (revmap_type == IRQ_HOST_MAP_LINEAR)
size += revmap_arg * sizeof(unsigned int);
host = kzalloc(size, GFP_KERNEL);
if (host == NULL)
return NULL;
/* Fill structure */
host->revmap_type = revmap_type;
host->inval_irq = inval_irq;
host->ops = ops;
host->of_node = of_node_get(of_node);
if (host->ops->match == NULL)
host->ops->match = default_irq_host_match;
raw_spin_lock_irqsave(&irq_big_lock, flags);
/* Check for the priority controller. */
if (revmap_type == IRQ_HOST_MAP_PRIORITY) {
if (irq_map[0].host != NULL) {
raw_spin_unlock_irqrestore(&irq_big_lock, flags);
of_node_put(host->of_node);
kfree(host);
return NULL;
}
irq_map[0].host = host;
}
list_add(&host->link, &irq_hosts);
raw_spin_unlock_irqrestore(&irq_big_lock, flags);
/* Additional setups per revmap type */
switch (revmap_type) {
case IRQ_HOST_MAP_PRIORITY:
/* 0 is always the invalid number for priority */
host->inval_irq = 0;
/* setup us as the host for all priority interrupts */
for (i = 1; i < NR_PRIORITY_IRQS; i++) {
irq_map[i].hwirq = i;
smp_wmb();
irq_map[i].host = host;
smp_wmb();
ops->map(host, i, i);
}
break;
case IRQ_HOST_MAP_LINEAR:
rmap = (unsigned int *)(host + 1);
for (i = 0; i < revmap_arg; i++)
rmap[i] = NO_IRQ;
host->revmap_data.linear.size = revmap_arg;
smp_wmb();
host->revmap_data.linear.revmap = rmap;
break;
case IRQ_HOST_MAP_TREE:
INIT_RADIX_TREE(&host->revmap_data.tree, GFP_KERNEL);
break;
default:
break;
}
pr_debug("irq: Allocated host of type %d @0x%p\n", revmap_type, host);
return host;
}
struct irq_host *irq_find_host(struct device_node *node)
{
struct irq_host *h, *found = NULL;
unsigned long flags;
/* We might want to match the legacy controller last since
* it might potentially be set to match all interrupts in
* the absence of a device node. This isn't a problem so far
* yet though...
*/
raw_spin_lock_irqsave(&irq_big_lock, flags);
list_for_each_entry(h, &irq_hosts, link)
if (h->ops->match(h, node)) {
found = h;
break;
}
raw_spin_unlock_irqrestore(&irq_big_lock, flags);
return found;
}
EXPORT_SYMBOL_GPL(irq_find_host);
void irq_set_default_host(struct irq_host *host)
{
pr_debug("irq: Default host set to @0x%p\n", host);
irq_default_host = host;
}
void irq_set_virq_count(unsigned int count)
{
pr_debug("irq: Trying to set virq count to %d\n", count);
BUG_ON(count < NR_PRIORITY_IRQS);
if (count < NR_IRQS)
irq_virq_count = count;
}
static int irq_setup_virq(struct irq_host *host, unsigned int virq,
irq_hw_number_t hwirq)
{
int res;
res = irq_alloc_desc_at(virq, 0);
if (res != virq) {
pr_debug("irq: -> allocating desc failed\n");
goto error;
}
/* map it */
smp_wmb();
irq_map[virq].hwirq = hwirq;
smp_mb();
if (host->ops->map(host, virq, hwirq)) {
pr_debug("irq: -> mapping failed, freeing\n");
goto errdesc;
}
irq_clear_status_flags(virq, IRQ_NOREQUEST);
return 0;
errdesc:
irq_free_descs(virq, 1);
error:
irq_free_virt(virq, 1);
return -1;
}
unsigned int irq_create_direct_mapping(struct irq_host *host)
{
unsigned int virq;
if (host == NULL)
host = irq_default_host;
BUG_ON(host == NULL);
WARN_ON(host->revmap_type != IRQ_HOST_MAP_NOMAP);
virq = irq_alloc_virt(host, 1, 0);
if (virq == NO_IRQ) {
pr_debug("irq: create_direct virq allocation failed\n");
return NO_IRQ;
}
pr_debug("irq: create_direct obtained virq %d\n", virq);
if (irq_setup_virq(host, virq, virq))
return NO_IRQ;
return virq;
}
unsigned int irq_create_mapping(struct irq_host *host,
irq_hw_number_t hwirq)
{
unsigned int virq, hint;
pr_debug("irq: irq_create_mapping(0x%p, 0x%lx)\n", host, hwirq);
/* Look for default host if nececssary */
if (host == NULL)
host = irq_default_host;
if (host == NULL) {
printk(KERN_WARNING "irq_create_mapping called for"
" NULL host, hwirq=%lx\n", hwirq);
WARN_ON(1);
return NO_IRQ;
}
pr_debug("irq: -> using host @%p\n", host);
/* Check if mapping already exists */
virq = irq_find_mapping(host, hwirq);
if (virq != NO_IRQ) {
pr_debug("irq: -> existing mapping on virq %d\n", virq);
return virq;
}
/* Allocate a virtual interrupt number */
hint = hwirq % irq_virq_count;
virq = irq_alloc_virt(host, 1, hint);
if (virq == NO_IRQ) {
pr_debug("irq: -> virq allocation failed\n");
return NO_IRQ;
}
if (irq_setup_virq(host, virq, hwirq))
return NO_IRQ;
pr_debug("irq: irq %lu on host %s mapped to virtual irq %u\n",
hwirq, host->of_node ? host->of_node->full_name : "null", virq);
return virq;
}
EXPORT_SYMBOL_GPL(irq_create_mapping);
unsigned int irq_create_of_mapping(struct device_node *controller,
const u32 *intspec, unsigned int intsize)
{
struct irq_host *host;
irq_hw_number_t hwirq;
unsigned int type = IRQ_TYPE_NONE;
unsigned int virq;
if (controller == NULL)
host = irq_default_host;
else
host = irq_find_host(controller);
if (host == NULL) {
printk(KERN_WARNING "irq: no irq host found for %s !\n",
controller->full_name);
return NO_IRQ;
}
/* If host has no translation, then we assume interrupt line */
if (host->ops->xlate == NULL)
hwirq = intspec[0];
else {
if (host->ops->xlate(host, controller, intspec, intsize,
&hwirq, &type))
return NO_IRQ;
}
/* Create mapping */
virq = irq_create_mapping(host, hwirq);
if (virq == NO_IRQ)
return virq;
/* Set type if specified and different than the current one */
if (type != IRQ_TYPE_NONE &&
type != (irqd_get_trigger_type(irq_get_irq_data(virq))))
irq_set_irq_type(virq, type);
return virq;
}
EXPORT_SYMBOL_GPL(irq_create_of_mapping);
void irq_dispose_mapping(unsigned int virq)
{
struct irq_host *host;
irq_hw_number_t hwirq;
if (virq == NO_IRQ)
return;
/* Never unmap priority interrupts */
if (virq < NR_PRIORITY_IRQS)
return;
host = irq_map[virq].host;
if (WARN_ON(host == NULL))
return;
irq_set_status_flags(virq, IRQ_NOREQUEST);
/* remove chip and handler */
irq_set_chip_and_handler(virq, NULL, NULL);
/* Make sure it's completed */
synchronize_irq(virq);
/* Tell the PIC about it */
if (host->ops->unmap)
host->ops->unmap(host, virq);
smp_mb();
/* Clear reverse map */
hwirq = irq_map[virq].hwirq;
switch (host->revmap_type) {
case IRQ_HOST_MAP_LINEAR:
if (hwirq < host->revmap_data.linear.size)
host->revmap_data.linear.revmap[hwirq] = NO_IRQ;
break;
case IRQ_HOST_MAP_TREE:
mutex_lock(&revmap_trees_mutex);
radix_tree_delete(&host->revmap_data.tree, hwirq);
mutex_unlock(&revmap_trees_mutex);
break;
}
/* Destroy map */
smp_mb();
irq_map[virq].hwirq = host->inval_irq;
irq_free_descs(virq, 1);
/* Free it */
irq_free_virt(virq, 1);
}
EXPORT_SYMBOL_GPL(irq_dispose_mapping);
unsigned int irq_find_mapping(struct irq_host *host,
irq_hw_number_t hwirq)
{
unsigned int i;
unsigned int hint = hwirq % irq_virq_count;
/* Look for default host if nececssary */
if (host == NULL)
host = irq_default_host;
if (host == NULL)
return NO_IRQ;
/* Slow path does a linear search of the map */
i = hint;
do {
if (irq_map[i].host == host &&
irq_map[i].hwirq == hwirq)
return i;
i++;
if (i >= irq_virq_count)
i = 4;
} while (i != hint);
return NO_IRQ;
}
EXPORT_SYMBOL_GPL(irq_find_mapping);
unsigned int irq_radix_revmap_lookup(struct irq_host *host,
irq_hw_number_t hwirq)
{
struct irq_map_entry *ptr;
unsigned int virq;
if (WARN_ON_ONCE(host->revmap_type != IRQ_HOST_MAP_TREE))
return irq_find_mapping(host, hwirq);
/*
* The ptr returned references the static global irq_map.
* but freeing an irq can delete nodes along the path to
* do the lookup via call_rcu.
*/
rcu_read_lock();
ptr = radix_tree_lookup(&host->revmap_data.tree, hwirq);
rcu_read_unlock();
/*
* If found in radix tree, then fine.
* Else fallback to linear lookup - this should not happen in practice
* as it means that we failed to insert the node in the radix tree.
*/
if (ptr)
virq = ptr - irq_map;
else
virq = irq_find_mapping(host, hwirq);
return virq;
}
void irq_radix_revmap_insert(struct irq_host *host, unsigned int virq,
irq_hw_number_t hwirq)
{
if (WARN_ON(host->revmap_type != IRQ_HOST_MAP_TREE))
return;
if (virq != NO_IRQ) {
mutex_lock(&revmap_trees_mutex);
radix_tree_insert(&host->revmap_data.tree, hwirq,
&irq_map[virq]);
mutex_unlock(&revmap_trees_mutex);
}
}
unsigned int irq_linear_revmap(struct irq_host *host,
irq_hw_number_t hwirq)
{
unsigned int *revmap;
if (WARN_ON_ONCE(host->revmap_type != IRQ_HOST_MAP_LINEAR))
return irq_find_mapping(host, hwirq);
/* Check revmap bounds */
if (unlikely(hwirq >= host->revmap_data.linear.size))
return irq_find_mapping(host, hwirq);
/* Check if revmap was allocated */
revmap = host->revmap_data.linear.revmap;
if (unlikely(revmap == NULL))
return irq_find_mapping(host, hwirq);
/* Fill up revmap with slow path if no mapping found */
if (unlikely(revmap[hwirq] == NO_IRQ))
revmap[hwirq] = irq_find_mapping(host, hwirq);
return revmap[hwirq];
}
unsigned int irq_alloc_virt(struct irq_host *host,
unsigned int count,
unsigned int hint)
{
unsigned long flags;
unsigned int i, j, found = NO_IRQ;
if (count == 0 || count > (irq_virq_count - NR_PRIORITY_IRQS))
return NO_IRQ;
raw_spin_lock_irqsave(&irq_big_lock, flags);
/* Use hint for 1 interrupt if any */
if (count == 1 && hint >= NR_PRIORITY_IRQS &&
hint < irq_virq_count && irq_map[hint].host == NULL) {
found = hint;
goto hint_found;
}
/* Look for count consecutive numbers in the allocatable
* (non-legacy) space
*/
for (i = NR_PRIORITY_IRQS, j = 0; i < irq_virq_count; i++) {
if (irq_map[i].host != NULL)
j = 0;
else
j++;
if (j == count) {
found = i - count + 1;
break;
}
}
if (found == NO_IRQ) {
raw_spin_unlock_irqrestore(&irq_big_lock, flags);
return NO_IRQ;
}
hint_found:
for (i = found; i < (found + count); i++) {
irq_map[i].hwirq = host->inval_irq;
smp_wmb();
irq_map[i].host = host;
}
raw_spin_unlock_irqrestore(&irq_big_lock, flags);
return found;
}
void irq_free_virt(unsigned int virq, unsigned int count)
{
unsigned long flags;
unsigned int i;
WARN_ON(virq < NR_PRIORITY_IRQS);
WARN_ON(count == 0 || (virq + count) > irq_virq_count);
if (virq < NR_PRIORITY_IRQS) {
if (virq + count < NR_PRIORITY_IRQS)
return;
count -= NR_PRIORITY_IRQS - virq;
virq = NR_PRIORITY_IRQS;
}
if (count > irq_virq_count || virq > irq_virq_count - count) {
if (virq > irq_virq_count)
return;
count = irq_virq_count - virq;
}
raw_spin_lock_irqsave(&irq_big_lock, flags);
for (i = virq; i < (virq + count); i++) {
struct irq_host *host;
host = irq_map[i].host;
irq_map[i].hwirq = host->inval_irq;
smp_wmb();
irq_map[i].host = NULL;
}
raw_spin_unlock_irqrestore(&irq_big_lock, flags);
}
#ifdef CONFIG_VIRQ_DEBUG
static int virq_debug_show(struct seq_file *m, void *private)
{
unsigned long flags;
struct irq_desc *desc;
const char *p;
static const char none[] = "none";
void *data;
int i;
seq_printf(m, "%-5s %-7s %-15s %-18s %s\n", "virq", "hwirq",
"chip name", "chip data", "host name");
for (i = 1; i < nr_irqs; i++) {
desc = irq_to_desc(i);
if (!desc)
continue;
raw_spin_lock_irqsave(&desc->lock, flags);
if (desc->action && desc->action->handler) {
struct irq_chip *chip;
seq_printf(m, "%5d ", i);
seq_printf(m, "0x%05lx ", irq_map[i].hwirq);
chip = irq_desc_get_chip(desc);
if (chip && chip->name)
p = chip->name;
else
p = none;
seq_printf(m, "%-15s ", p);
data = irq_desc_get_chip_data(desc);
seq_printf(m, "0x%16p ", data);
if (irq_map[i].host && irq_map[i].host->of_node)
p = irq_map[i].host->of_node->full_name;
else
p = none;
seq_printf(m, "%s\n", p);
}
raw_spin_unlock_irqrestore(&desc->lock, flags);
}
return 0;
}
static int virq_debug_open(struct inode *inode, struct file *file)
{
return single_open(file, virq_debug_show, inode->i_private);
}
static const struct file_operations virq_debug_fops = {
.open = virq_debug_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int __init irq_debugfs_init(void)
{
if (debugfs_create_file("virq_mapping", S_IRUGO, powerpc_debugfs_root,
NULL, &virq_debug_fops) == NULL)
return -ENOMEM;
return 0;
}
device_initcall(irq_debugfs_init);
#endif /* CONFIG_VIRQ_DEBUG */
......@@ -48,7 +48,7 @@ struct megamod_regs {
};
struct megamod_pic {
struct irq_host *irqhost;
struct irq_domain *irqhost;
struct megamod_regs __iomem *regs;
raw_spinlock_t lock;
......@@ -116,7 +116,7 @@ static void megamod_irq_cascade(unsigned int irq, struct irq_desc *desc)
}
}
static int megamod_map(struct irq_host *h, unsigned int virq,
static int megamod_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
struct megamod_pic *pic = h->host_data;
......@@ -136,21 +136,9 @@ static int megamod_map(struct irq_host *h, unsigned int virq,
return 0;
}
static int megamod_xlate(struct irq_host *h, struct device_node *ct,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_type)
{
/* megamod intspecs must have 1 cell */
BUG_ON(intsize != 1);
*out_hwirq = intspec[0];
*out_type = IRQ_TYPE_NONE;
return 0;
}
static struct irq_host_ops megamod_host_ops = {
static const struct irq_domain_ops megamod_domain_ops = {
.map = megamod_map,
.xlate = megamod_xlate,
.xlate = irq_domain_xlate_onecell,
};
static void __init set_megamod_mux(struct megamod_pic *pic, int src, int output)
......@@ -223,9 +211,8 @@ static struct megamod_pic * __init init_megamod_pic(struct device_node *np)
return NULL;
}
pic->irqhost = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR,
NR_COMBINERS * 32, &megamod_host_ops,
IRQ_UNMAPPED);
pic->irqhost = irq_domain_add_linear(np, NR_COMBINERS * 32,
&megamod_domain_ops, pic);
if (!pic->irqhost) {
pr_err("%s: Could not alloc host.\n", np->full_name);
goto error_free;
......
......@@ -14,6 +14,7 @@ config MICROBLAZE
select TRACING_SUPPORT
select OF
select OF_EARLY_FLATTREE
select IRQ_DOMAIN
select HAVE_GENERIC_HARDIRQS
select GENERIC_IRQ_PROBE
select GENERIC_IRQ_SHOW
......
/*
* Copyright (C) 2006 Atmark Techno, Inc.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
#ifndef _ASM_MICROBLAZE_HARDIRQ_H
#define _ASM_MICROBLAZE_HARDIRQ_H
/* should be defined in each interrupt controller driver */
extern unsigned int get_irq(struct pt_regs *regs);
#include <asm-generic/hardirq.h>
#endif /* _ASM_MICROBLAZE_HARDIRQ_H */
......@@ -9,49 +9,13 @@
#ifndef _ASM_MICROBLAZE_IRQ_H
#define _ASM_MICROBLAZE_IRQ_H
/*
* Linux IRQ# is currently offset by one to map to the hardware
* irq number. So hardware IRQ0 maps to Linux irq 1.
*/
#define NO_IRQ_OFFSET 1
#define IRQ_OFFSET NO_IRQ_OFFSET
#define NR_IRQS (32 + IRQ_OFFSET)
#define NR_IRQS (32 + 1)
#include <asm-generic/irq.h>
/* This type is the placeholder for a hardware interrupt number. It has to
* be big enough to enclose whatever representation is used by a given
* platform.
*/
typedef unsigned long irq_hw_number_t;
extern unsigned int nr_irq;
struct pt_regs;
extern void do_IRQ(struct pt_regs *regs);
/** FIXME - not implement
* irq_dispose_mapping - Unmap an interrupt
* @virq: linux virq number of the interrupt to unmap
*/
static inline void irq_dispose_mapping(unsigned int virq)
{
return;
}
struct irq_host;
/**
* irq_create_mapping - Map a hardware interrupt into linux virq space
* @host: host owning this hardware interrupt or NULL for default host
* @hwirq: hardware irq number in that host space
*
* Only one mapping per hardware interrupt is permitted. Returns a linux
* virq number.
* If the sense/trigger is to be specified, set_irq_type() should be called
* on the number returned from that call.
*/
extern unsigned int irq_create_mapping(struct irq_host *host,
irq_hw_number_t hwirq);
/* should be defined in each interrupt controller driver */
extern unsigned int get_irq(void);
#endif /* _ASM_MICROBLAZE_IRQ_H */
......@@ -9,6 +9,7 @@
*/
#include <linux/init.h>
#include <linux/irqdomain.h>
#include <linux/irq.h>
#include <asm/page.h>
#include <linux/io.h>
......@@ -25,8 +26,6 @@ static unsigned int intc_baseaddr;
#define INTC_BASE intc_baseaddr
#endif
unsigned int nr_irq;
/* No one else should require these constants, so define them locally here. */
#define ISR 0x00 /* Interrupt Status Register */
#define IPR 0x04 /* Interrupt Pending Register */
......@@ -84,24 +83,45 @@ static struct irq_chip intc_dev = {
.irq_mask_ack = intc_mask_ack,
};
unsigned int get_irq(struct pt_regs *regs)
static struct irq_domain *root_domain;
unsigned int get_irq(void)
{
int irq;
unsigned int hwirq, irq = -1;
/*
* NOTE: This function is the one that needs to be improved in
* order to handle multiple interrupt controllers. It currently
* is hardcoded to check for interrupts only on the first INTC.
*/
irq = in_be32(INTC_BASE + IVR) + NO_IRQ_OFFSET;
pr_debug("get_irq: %d\n", irq);
hwirq = in_be32(INTC_BASE + IVR);
if (hwirq != -1U)
irq = irq_find_mapping(root_domain, hwirq);
pr_debug("get_irq: hwirq=%d, irq=%d\n", hwirq, irq);
return irq;
}
int xintc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
{
u32 intr_mask = (u32)d->host_data;
if (intr_mask & (1 << hw)) {
irq_set_chip_and_handler_name(irq, &intc_dev,
handle_edge_irq, "edge");
irq_clear_status_flags(irq, IRQ_LEVEL);
} else {
irq_set_chip_and_handler_name(irq, &intc_dev,
handle_level_irq, "level");
irq_set_status_flags(irq, IRQ_LEVEL);
}
return 0;
}
static const struct irq_domain_ops xintc_irq_domain_ops = {
.xlate = irq_domain_xlate_onetwocell,
.map = xintc_map,
};
void __init init_IRQ(void)
{
u32 i, intr_mask;
u32 nr_irq, intr_mask;
struct device_node *intc = NULL;
#ifdef CONFIG_SELFMOD_INTC
unsigned int intc_baseaddr = 0;
......@@ -146,16 +166,9 @@ void __init init_IRQ(void)
/* Turn on the Master Enable. */
out_be32(intc_baseaddr + MER, MER_HIE | MER_ME);
for (i = IRQ_OFFSET; i < (nr_irq + IRQ_OFFSET); ++i) {
if (intr_mask & (0x00000001 << (i - IRQ_OFFSET))) {
irq_set_chip_and_handler_name(i, &intc_dev,
handle_edge_irq, "edge");
irq_clear_status_flags(i, IRQ_LEVEL);
} else {
irq_set_chip_and_handler_name(i, &intc_dev,
handle_level_irq, "level");
irq_set_status_flags(i, IRQ_LEVEL);
}
irq_get_irq_data(i)->hwirq = i - IRQ_OFFSET;
}
/* Yeah, okay, casting the intr_mask to a void* is butt-ugly, but I'm
* lazy and Michal can clean it up to something nicer when he tests
* and commits this patch. ~~gcl */
root_domain = irq_domain_add_linear(intc, nr_irq, &xintc_irq_domain_ops,
(void *)intr_mask);
}
......@@ -31,14 +31,13 @@ void __irq_entry do_IRQ(struct pt_regs *regs)
trace_hardirqs_off();
irq_enter();
irq = get_irq(regs);
irq = get_irq();
next_irq:
BUG_ON(!irq);
/* Substract 1 because of get_irq */
generic_handle_irq(irq + IRQ_OFFSET - NO_IRQ_OFFSET);
generic_handle_irq(irq);
irq = get_irq(regs);
if (irq) {
irq = get_irq();
if (irq != -1U) {
pr_debug("next irq: %d\n", irq);
++concurrent_irq;
goto next_irq;
......@@ -48,18 +47,3 @@ void __irq_entry do_IRQ(struct pt_regs *regs)
set_irq_regs(old_regs);
trace_hardirqs_on();
}
/* MS: There is no any advance mapping mechanism. We are using simple 32bit
intc without any cascades or any connection that's why mapping is 1:1 */
unsigned int irq_create_mapping(struct irq_host *host, irq_hw_number_t hwirq)
{
return hwirq + IRQ_OFFSET;
}
EXPORT_SYMBOL_GPL(irq_create_mapping);
unsigned int irq_create_of_mapping(struct device_node *controller,
const u32 *intspec, unsigned int intsize)
{
return intspec[0] + IRQ_OFFSET;
}
EXPORT_SYMBOL_GPL(irq_create_of_mapping);
......@@ -51,8 +51,6 @@ void __init setup_arch(char **cmdline_p)
unflatten_device_tree();
/* NOTE I think that this function is not necessary to call */
/* irq_early_init(); */
setup_cpuinfo();
microblaze_cache_init();
......
......@@ -2327,6 +2327,7 @@ config USE_OF
bool "Flattened Device Tree support"
select OF
select OF_EARLY_FLATTREE
select IRQ_DOMAIN
help
Include support for flattened device tree machine descriptions.
......
......@@ -11,15 +11,12 @@
#include <linux/linkage.h>
#include <linux/smp.h>
#include <linux/irqdomain.h>
#include <asm/mipsmtregs.h>
#include <irq.h>
static inline void irq_dispose_mapping(unsigned int virq)
{
}
#ifdef CONFIG_I8259
static inline int irq_canonicalize(int irq)
{
......
......@@ -60,20 +60,6 @@ void __init early_init_dt_setup_initrd_arch(unsigned long start,
}
#endif
/*
* irq_create_of_mapping - Hook to resolve OF irq specifier into a Linux irq#
*
* Currently the mapping mechanism is trivial; simple flat hwirq numbers are
* mapped 1:1 onto Linux irq numbers. Cascaded irq controllers are not
* supported.
*/
unsigned int irq_create_of_mapping(struct device_node *controller,
const u32 *intspec, unsigned int intsize)
{
return intspec[0];
}
EXPORT_SYMBOL_GPL(irq_create_of_mapping);
void __init early_init_devtree(void *params)
{
/* Setup flat device-tree pointer */
......
......@@ -24,6 +24,7 @@
#include <linux/types.h>
#include <asm/irq.h>
#include <linux/irqdomain.h>
#include <linux/atomic.h>
#include <linux/of_irq.h>
#include <linux/of_fdt.h>
......@@ -63,15 +64,6 @@ extern const void *of_get_mac_address(struct device_node *np);
struct pci_dev;
extern int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq);
/* This routine is here to provide compatibility with how powerpc
* handles IRQ mapping for OF device nodes. We precompute and permanently
* register them in the platform_device objects, whereas powerpc computes them
* on request.
*/
static inline void irq_dispose_mapping(unsigned int virq)
{
}
#endif /* __ASSEMBLY__ */
#endif /* __KERNEL__ */
#endif /* _ASM_OPENRISC_PROM_H */
......@@ -135,6 +135,7 @@ config PPC
select HAVE_GENERIC_HARDIRQS
select HAVE_SPARSE_IRQ
select IRQ_PER_CPU
select IRQ_DOMAIN
select GENERIC_IRQ_SHOW
select GENERIC_IRQ_SHOW_LEVEL
select IRQ_FORCED_THREADING
......
......@@ -25,7 +25,7 @@
struct ehv_pic {
/* The remapper for this EHV_PIC */
struct irq_host *irqhost;
struct irq_domain *irqhost;
/* The "linux" controller struct */
struct irq_chip hc_irq;
......
......@@ -6,7 +6,7 @@
extern void i8259_init(struct device_node *node, unsigned long intack_addr);
extern unsigned int i8259_irq(void);
extern struct irq_host *i8259_get_host(void);
extern struct irq_domain *i8259_get_host(void);
#endif /* __KERNEL__ */
#endif /* _ASM_POWERPC_I8259_H */
......@@ -9,6 +9,7 @@
* 2 of the License, or (at your option) any later version.
*/
#include <linux/irqdomain.h>
#include <linux/threads.h>
#include <linux/list.h>
#include <linux/radix-tree.h>
......@@ -35,258 +36,12 @@ extern atomic_t ppc_n_lost_interrupts;
/* Total number of virq in the platform */
#define NR_IRQS CONFIG_NR_IRQS
/* Number of irqs reserved for the legacy controller */
#define NUM_ISA_INTERRUPTS 16
/* Same thing, used by the generic IRQ code */
#define NR_IRQS_LEGACY NUM_ISA_INTERRUPTS
/* This type is the placeholder for a hardware interrupt number. It has to
* be big enough to enclose whatever representation is used by a given
* platform.
*/
typedef unsigned long irq_hw_number_t;
/* Interrupt controller "host" data structure. This could be defined as a
* irq domain controller. That is, it handles the mapping between hardware
* and virtual interrupt numbers for a given interrupt domain. The host
* structure is generally created by the PIC code for a given PIC instance
* (though a host can cover more than one PIC if they have a flat number
* model). It's the host callbacks that are responsible for setting the
* irq_chip on a given irq_desc after it's been mapped.
*
* The host code and data structures are fairly agnostic to the fact that
* we use an open firmware device-tree. We do have references to struct
* device_node in two places: in irq_find_host() to find the host matching
* a given interrupt controller node, and of course as an argument to its
* counterpart host->ops->match() callback. However, those are treated as
* generic pointers by the core and the fact that it's actually a device-node
* pointer is purely a convention between callers and implementation. This
* code could thus be used on other architectures by replacing those two
* by some sort of arch-specific void * "token" used to identify interrupt
* controllers.
*/
struct irq_host;
struct radix_tree_root;
/* Functions below are provided by the host and called whenever a new mapping
* is created or an old mapping is disposed. The host can then proceed to
* whatever internal data structures management is required. It also needs
* to setup the irq_desc when returning from map().
*/
struct irq_host_ops {
/* Match an interrupt controller device node to a host, returns
* 1 on a match
*/
int (*match)(struct irq_host *h, struct device_node *node);
/* Create or update a mapping between a virtual irq number and a hw
* irq number. This is called only once for a given mapping.
*/
int (*map)(struct irq_host *h, unsigned int virq, irq_hw_number_t hw);
/* Dispose of such a mapping */
void (*unmap)(struct irq_host *h, unsigned int virq);
/* Translate device-tree interrupt specifier from raw format coming
* from the firmware to a irq_hw_number_t (interrupt line number) and
* type (sense) that can be passed to set_irq_type(). In the absence
* of this callback, irq_create_of_mapping() and irq_of_parse_and_map()
* will return the hw number in the first cell and IRQ_TYPE_NONE for
* the type (which amount to keeping whatever default value the
* interrupt controller has for that line)
*/
int (*xlate)(struct irq_host *h, struct device_node *ctrler,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_type);
};
struct irq_host {
struct list_head link;
/* type of reverse mapping technique */
unsigned int revmap_type;
#define IRQ_HOST_MAP_LEGACY 0 /* legacy 8259, gets irqs 1..15 */
#define IRQ_HOST_MAP_NOMAP 1 /* no fast reverse mapping */
#define IRQ_HOST_MAP_LINEAR 2 /* linear map of interrupts */
#define IRQ_HOST_MAP_TREE 3 /* radix tree */
union {
struct {
unsigned int size;
unsigned int *revmap;
} linear;
struct radix_tree_root tree;
} revmap_data;
struct irq_host_ops *ops;
void *host_data;
irq_hw_number_t inval_irq;
/* Optional device node pointer */
struct device_node *of_node;
};
struct irq_data;
extern irq_hw_number_t irqd_to_hwirq(struct irq_data *d);
extern irq_hw_number_t virq_to_hw(unsigned int virq);
extern bool virq_is_host(unsigned int virq, struct irq_host *host);
/**
* irq_alloc_host - Allocate a new irq_host data structure
* @of_node: optional device-tree node of the interrupt controller
* @revmap_type: type of reverse mapping to use
* @revmap_arg: for IRQ_HOST_MAP_LINEAR linear only: size of the map
* @ops: map/unmap host callbacks
* @inval_irq: provide a hw number in that host space that is always invalid
*
* Allocates and initialize and irq_host structure. Note that in the case of
* IRQ_HOST_MAP_LEGACY, the map() callback will be called before this returns
* for all legacy interrupts except 0 (which is always the invalid irq for
* a legacy controller). For a IRQ_HOST_MAP_LINEAR, the map is allocated by
* this call as well. For a IRQ_HOST_MAP_TREE, the radix tree will be allocated
* later during boot automatically (the reverse mapping will use the slow path
* until that happens).
*/
extern struct irq_host *irq_alloc_host(struct device_node *of_node,
unsigned int revmap_type,
unsigned int revmap_arg,
struct irq_host_ops *ops,
irq_hw_number_t inval_irq);
/**
* irq_find_host - Locates a host for a given device node
* @node: device-tree node of the interrupt controller
*/
extern struct irq_host *irq_find_host(struct device_node *node);
/**
* irq_set_default_host - Set a "default" host
* @host: default host pointer
*
* For convenience, it's possible to set a "default" host that will be used
* whenever NULL is passed to irq_create_mapping(). It makes life easier for
* platforms that want to manipulate a few hard coded interrupt numbers that
* aren't properly represented in the device-tree.
*/
extern void irq_set_default_host(struct irq_host *host);
/**
* irq_set_virq_count - Set the maximum number of virt irqs
* @count: number of linux virtual irqs, capped with NR_IRQS
*
* This is mainly for use by platforms like iSeries who want to program
* the virtual irq number in the controller to avoid the reverse mapping
*/
extern void irq_set_virq_count(unsigned int count);
/**
* irq_create_mapping - Map a hardware interrupt into linux virq space
* @host: host owning this hardware interrupt or NULL for default host
* @hwirq: hardware irq number in that host space
*
* Only one mapping per hardware interrupt is permitted. Returns a linux
* virq number.
* If the sense/trigger is to be specified, set_irq_type() should be called
* on the number returned from that call.
*/
extern unsigned int irq_create_mapping(struct irq_host *host,
irq_hw_number_t hwirq);
/**
* irq_dispose_mapping - Unmap an interrupt
* @virq: linux virq number of the interrupt to unmap
*/
extern void irq_dispose_mapping(unsigned int virq);
/**
* irq_find_mapping - Find a linux virq from an hw irq number.
* @host: host owning this hardware interrupt
* @hwirq: hardware irq number in that host space
*
* This is a slow path, for use by generic code. It's expected that an
* irq controller implementation directly calls the appropriate low level
* mapping function.
*/
extern unsigned int irq_find_mapping(struct irq_host *host,
irq_hw_number_t hwirq);
/**
* irq_create_direct_mapping - Allocate a virq for direct mapping
* @host: host to allocate the virq for or NULL for default host
*
* This routine is used for irq controllers which can choose the hardware
* interrupt numbers they generate. In such a case it's simplest to use
* the linux virq as the hardware interrupt number.
*/
extern unsigned int irq_create_direct_mapping(struct irq_host *host);
/**
* irq_radix_revmap_insert - Insert a hw irq to linux virq number mapping.
* @host: host owning this hardware interrupt
* @virq: linux irq number
* @hwirq: hardware irq number in that host space
*
* This is for use by irq controllers that use a radix tree reverse
* mapping for fast lookup.
*/
extern void irq_radix_revmap_insert(struct irq_host *host, unsigned int virq,
irq_hw_number_t hwirq);
/**
* irq_radix_revmap_lookup - Find a linux virq from a hw irq number.
* @host: host owning this hardware interrupt
* @hwirq: hardware irq number in that host space
*
* This is a fast path, for use by irq controller code that uses radix tree
* revmaps
*/
extern unsigned int irq_radix_revmap_lookup(struct irq_host *host,
irq_hw_number_t hwirq);
/**
* irq_linear_revmap - Find a linux virq from a hw irq number.
* @host: host owning this hardware interrupt
* @hwirq: hardware irq number in that host space
*
* This is a fast path, for use by irq controller code that uses linear
* revmaps. It does fallback to the slow path if the revmap doesn't exist
* yet and will create the revmap entry with appropriate locking
*/
extern unsigned int irq_linear_revmap(struct irq_host *host,
irq_hw_number_t hwirq);
/**
* irq_alloc_virt - Allocate virtual irq numbers
* @host: host owning these new virtual irqs
* @count: number of consecutive numbers to allocate
* @hint: pass a hint number, the allocator will try to use a 1:1 mapping
*
* This is a low level function that is used internally by irq_create_mapping()
* and that can be used by some irq controllers implementations for things
* like allocating ranges of numbers for MSIs. The revmaps are left untouched.
*/
extern unsigned int irq_alloc_virt(struct irq_host *host,
unsigned int count,
unsigned int hint);
/**
* irq_free_virt - Free virtual irq numbers
* @virq: virtual irq number of the first interrupt to free
* @count: number of interrupts to free
*
* This function is the opposite of irq_alloc_virt. It will not clear reverse
* maps, this should be done previously by unmap'ing the interrupt. In fact,
* all interrupts covered by the range being freed should have been unmapped
* prior to calling this.
*/
extern void irq_free_virt(unsigned int virq, unsigned int count);
/**
* irq_early_init - Init irq remapping subsystem
......
......@@ -255,7 +255,7 @@ struct mpic
struct device_node *node;
/* The remapper for this MPIC */
struct irq_host *irqhost;
struct irq_domain *irqhost;
/* The "linux" controller struct */
struct irq_chip hc_irq;
......
......@@ -86,7 +86,7 @@ struct ics {
extern unsigned int xics_default_server;
extern unsigned int xics_default_distrib_server;
extern unsigned int xics_interrupt_server_size;
extern struct irq_host *xics_host;
extern struct irq_domain *xics_host;
struct xics_cppr {
unsigned char stack[MAX_NUM_PRIORITIES];
......
......@@ -490,409 +490,19 @@ void do_softirq(void)
local_irq_restore(flags);
}
/*
* IRQ controller and virtual interrupts
*/
/* The main irq map itself is an array of NR_IRQ entries containing the
* associate host and irq number. An entry with a host of NULL is free.
* An entry can be allocated if it's free, the allocator always then sets
* hwirq first to the host's invalid irq number and then fills ops.
*/
struct irq_map_entry {
irq_hw_number_t hwirq;
struct irq_host *host;
};
static LIST_HEAD(irq_hosts);
static DEFINE_RAW_SPINLOCK(irq_big_lock);
static DEFINE_MUTEX(revmap_trees_mutex);
static struct irq_map_entry irq_map[NR_IRQS];
static unsigned int irq_virq_count = NR_IRQS;
static struct irq_host *irq_default_host;
irq_hw_number_t irqd_to_hwirq(struct irq_data *d)
{
return irq_map[d->irq].hwirq;
return d->hwirq;
}
EXPORT_SYMBOL_GPL(irqd_to_hwirq);
irq_hw_number_t virq_to_hw(unsigned int virq)
{
return irq_map[virq].hwirq;
struct irq_data *irq_data = irq_get_irq_data(virq);
return WARN_ON(!irq_data) ? 0 : irq_data->hwirq;
}
EXPORT_SYMBOL_GPL(virq_to_hw);
bool virq_is_host(unsigned int virq, struct irq_host *host)
{
return irq_map[virq].host == host;
}
EXPORT_SYMBOL_GPL(virq_is_host);
static int default_irq_host_match(struct irq_host *h, struct device_node *np)
{
return h->of_node != NULL && h->of_node == np;
}
struct irq_host *irq_alloc_host(struct device_node *of_node,
unsigned int revmap_type,
unsigned int revmap_arg,
struct irq_host_ops *ops,
irq_hw_number_t inval_irq)
{
struct irq_host *host;
unsigned int size = sizeof(struct irq_host);
unsigned int i;
unsigned int *rmap;
unsigned long flags;
/* Allocate structure and revmap table if using linear mapping */
if (revmap_type == IRQ_HOST_MAP_LINEAR)
size += revmap_arg * sizeof(unsigned int);
host = kzalloc(size, GFP_KERNEL);
if (host == NULL)
return NULL;
/* Fill structure */
host->revmap_type = revmap_type;
host->inval_irq = inval_irq;
host->ops = ops;
host->of_node = of_node_get(of_node);
if (host->ops->match == NULL)
host->ops->match = default_irq_host_match;
raw_spin_lock_irqsave(&irq_big_lock, flags);
/* If it's a legacy controller, check for duplicates and
* mark it as allocated (we use irq 0 host pointer for that
*/
if (revmap_type == IRQ_HOST_MAP_LEGACY) {
if (irq_map[0].host != NULL) {
raw_spin_unlock_irqrestore(&irq_big_lock, flags);
of_node_put(host->of_node);
kfree(host);
return NULL;
}
irq_map[0].host = host;
}
list_add(&host->link, &irq_hosts);
raw_spin_unlock_irqrestore(&irq_big_lock, flags);
/* Additional setups per revmap type */
switch(revmap_type) {
case IRQ_HOST_MAP_LEGACY:
/* 0 is always the invalid number for legacy */
host->inval_irq = 0;
/* setup us as the host for all legacy interrupts */
for (i = 1; i < NUM_ISA_INTERRUPTS; i++) {
irq_map[i].hwirq = i;
smp_wmb();
irq_map[i].host = host;
smp_wmb();
/* Legacy flags are left to default at this point,
* one can then use irq_create_mapping() to
* explicitly change them
*/
ops->map(host, i, i);
/* Clear norequest flags */
irq_clear_status_flags(i, IRQ_NOREQUEST);
}
break;
case IRQ_HOST_MAP_LINEAR:
rmap = (unsigned int *)(host + 1);
for (i = 0; i < revmap_arg; i++)
rmap[i] = NO_IRQ;
host->revmap_data.linear.size = revmap_arg;
smp_wmb();
host->revmap_data.linear.revmap = rmap;
break;
case IRQ_HOST_MAP_TREE:
INIT_RADIX_TREE(&host->revmap_data.tree, GFP_KERNEL);
break;
default:
break;
}
pr_debug("irq: Allocated host of type %d @0x%p\n", revmap_type, host);
return host;
}
struct irq_host *irq_find_host(struct device_node *node)
{
struct irq_host *h, *found = NULL;
unsigned long flags;
/* We might want to match the legacy controller last since
* it might potentially be set to match all interrupts in
* the absence of a device node. This isn't a problem so far
* yet though...
*/
raw_spin_lock_irqsave(&irq_big_lock, flags);
list_for_each_entry(h, &irq_hosts, link)
if (h->ops->match(h, node)) {
found = h;
break;
}
raw_spin_unlock_irqrestore(&irq_big_lock, flags);
return found;
}
EXPORT_SYMBOL_GPL(irq_find_host);
void irq_set_default_host(struct irq_host *host)
{
pr_debug("irq: Default host set to @0x%p\n", host);
irq_default_host = host;
}
void irq_set_virq_count(unsigned int count)
{
pr_debug("irq: Trying to set virq count to %d\n", count);
BUG_ON(count < NUM_ISA_INTERRUPTS);
if (count < NR_IRQS)
irq_virq_count = count;
}
static int irq_setup_virq(struct irq_host *host, unsigned int virq,
irq_hw_number_t hwirq)
{
int res;
res = irq_alloc_desc_at(virq, 0);
if (res != virq) {
pr_debug("irq: -> allocating desc failed\n");
goto error;
}
/* map it */
smp_wmb();
irq_map[virq].hwirq = hwirq;
smp_mb();
if (host->ops->map(host, virq, hwirq)) {
pr_debug("irq: -> mapping failed, freeing\n");
goto errdesc;
}
irq_clear_status_flags(virq, IRQ_NOREQUEST);
return 0;
errdesc:
irq_free_descs(virq, 1);
error:
irq_free_virt(virq, 1);
return -1;
}
unsigned int irq_create_direct_mapping(struct irq_host *host)
{
unsigned int virq;
if (host == NULL)
host = irq_default_host;
BUG_ON(host == NULL);
WARN_ON(host->revmap_type != IRQ_HOST_MAP_NOMAP);
virq = irq_alloc_virt(host, 1, 0);
if (virq == NO_IRQ) {
pr_debug("irq: create_direct virq allocation failed\n");
return NO_IRQ;
}
pr_debug("irq: create_direct obtained virq %d\n", virq);
if (irq_setup_virq(host, virq, virq))
return NO_IRQ;
return virq;
}
unsigned int irq_create_mapping(struct irq_host *host,
irq_hw_number_t hwirq)
{
unsigned int virq, hint;
pr_debug("irq: irq_create_mapping(0x%p, 0x%lx)\n", host, hwirq);
/* Look for default host if nececssary */
if (host == NULL)
host = irq_default_host;
if (host == NULL) {
printk(KERN_WARNING "irq_create_mapping called for"
" NULL host, hwirq=%lx\n", hwirq);
WARN_ON(1);
return NO_IRQ;
}
pr_debug("irq: -> using host @%p\n", host);
/* Check if mapping already exists */
virq = irq_find_mapping(host, hwirq);
if (virq != NO_IRQ) {
pr_debug("irq: -> existing mapping on virq %d\n", virq);
return virq;
}
/* Get a virtual interrupt number */
if (host->revmap_type == IRQ_HOST_MAP_LEGACY) {
/* Handle legacy */
virq = (unsigned int)hwirq;
if (virq == 0 || virq >= NUM_ISA_INTERRUPTS)
return NO_IRQ;
return virq;
} else {
/* Allocate a virtual interrupt number */
hint = hwirq % irq_virq_count;
virq = irq_alloc_virt(host, 1, hint);
if (virq == NO_IRQ) {
pr_debug("irq: -> virq allocation failed\n");
return NO_IRQ;
}
}
if (irq_setup_virq(host, virq, hwirq))
return NO_IRQ;
pr_debug("irq: irq %lu on host %s mapped to virtual irq %u\n",
hwirq, host->of_node ? host->of_node->full_name : "null", virq);
return virq;
}
EXPORT_SYMBOL_GPL(irq_create_mapping);
unsigned int irq_create_of_mapping(struct device_node *controller,
const u32 *intspec, unsigned int intsize)
{
struct irq_host *host;
irq_hw_number_t hwirq;
unsigned int type = IRQ_TYPE_NONE;
unsigned int virq;
if (controller == NULL)
host = irq_default_host;
else
host = irq_find_host(controller);
if (host == NULL) {
printk(KERN_WARNING "irq: no irq host found for %s !\n",
controller->full_name);
return NO_IRQ;
}
/* If host has no translation, then we assume interrupt line */
if (host->ops->xlate == NULL)
hwirq = intspec[0];
else {
if (host->ops->xlate(host, controller, intspec, intsize,
&hwirq, &type))
return NO_IRQ;
}
/* Create mapping */
virq = irq_create_mapping(host, hwirq);
if (virq == NO_IRQ)
return virq;
/* Set type if specified and different than the current one */
if (type != IRQ_TYPE_NONE &&
type != (irqd_get_trigger_type(irq_get_irq_data(virq))))
irq_set_irq_type(virq, type);
return virq;
}
EXPORT_SYMBOL_GPL(irq_create_of_mapping);
void irq_dispose_mapping(unsigned int virq)
{
struct irq_host *host;
irq_hw_number_t hwirq;
if (virq == NO_IRQ)
return;
host = irq_map[virq].host;
if (WARN_ON(host == NULL))
return;
/* Never unmap legacy interrupts */
if (host->revmap_type == IRQ_HOST_MAP_LEGACY)
return;
irq_set_status_flags(virq, IRQ_NOREQUEST);
/* remove chip and handler */
irq_set_chip_and_handler(virq, NULL, NULL);
/* Make sure it's completed */
synchronize_irq(virq);
/* Tell the PIC about it */
if (host->ops->unmap)
host->ops->unmap(host, virq);
smp_mb();
/* Clear reverse map */
hwirq = irq_map[virq].hwirq;
switch(host->revmap_type) {
case IRQ_HOST_MAP_LINEAR:
if (hwirq < host->revmap_data.linear.size)
host->revmap_data.linear.revmap[hwirq] = NO_IRQ;
break;
case IRQ_HOST_MAP_TREE:
mutex_lock(&revmap_trees_mutex);
radix_tree_delete(&host->revmap_data.tree, hwirq);
mutex_unlock(&revmap_trees_mutex);
break;
}
/* Destroy map */
smp_mb();
irq_map[virq].hwirq = host->inval_irq;
irq_free_descs(virq, 1);
/* Free it */
irq_free_virt(virq, 1);
}
EXPORT_SYMBOL_GPL(irq_dispose_mapping);
unsigned int irq_find_mapping(struct irq_host *host,
irq_hw_number_t hwirq)
{
unsigned int i;
unsigned int hint = hwirq % irq_virq_count;
/* Look for default host if nececssary */
if (host == NULL)
host = irq_default_host;
if (host == NULL)
return NO_IRQ;
/* legacy -> bail early */
if (host->revmap_type == IRQ_HOST_MAP_LEGACY)
return hwirq;
/* Slow path does a linear search of the map */
if (hint < NUM_ISA_INTERRUPTS)
hint = NUM_ISA_INTERRUPTS;
i = hint;
do {
if (irq_map[i].host == host &&
irq_map[i].hwirq == hwirq)
return i;
i++;
if (i >= irq_virq_count)
i = NUM_ISA_INTERRUPTS;
} while(i != hint);
return NO_IRQ;
}
EXPORT_SYMBOL_GPL(irq_find_mapping);
#ifdef CONFIG_SMP
int irq_choose_cpu(const struct cpumask *mask)
{
......@@ -929,232 +539,11 @@ int irq_choose_cpu(const struct cpumask *mask)
}
#endif
unsigned int irq_radix_revmap_lookup(struct irq_host *host,
irq_hw_number_t hwirq)
{
struct irq_map_entry *ptr;
unsigned int virq;
if (WARN_ON_ONCE(host->revmap_type != IRQ_HOST_MAP_TREE))
return irq_find_mapping(host, hwirq);
/*
* The ptr returned references the static global irq_map.
* but freeing an irq can delete nodes along the path to
* do the lookup via call_rcu.
*/
rcu_read_lock();
ptr = radix_tree_lookup(&host->revmap_data.tree, hwirq);
rcu_read_unlock();
/*
* If found in radix tree, then fine.
* Else fallback to linear lookup - this should not happen in practice
* as it means that we failed to insert the node in the radix tree.
*/
if (ptr)
virq = ptr - irq_map;
else
virq = irq_find_mapping(host, hwirq);
return virq;
}
void irq_radix_revmap_insert(struct irq_host *host, unsigned int virq,
irq_hw_number_t hwirq)
{
if (WARN_ON(host->revmap_type != IRQ_HOST_MAP_TREE))
return;
if (virq != NO_IRQ) {
mutex_lock(&revmap_trees_mutex);
radix_tree_insert(&host->revmap_data.tree, hwirq,
&irq_map[virq]);
mutex_unlock(&revmap_trees_mutex);
}
}
unsigned int irq_linear_revmap(struct irq_host *host,
irq_hw_number_t hwirq)
{
unsigned int *revmap;
if (WARN_ON_ONCE(host->revmap_type != IRQ_HOST_MAP_LINEAR))
return irq_find_mapping(host, hwirq);
/* Check revmap bounds */
if (unlikely(hwirq >= host->revmap_data.linear.size))
return irq_find_mapping(host, hwirq);
/* Check if revmap was allocated */
revmap = host->revmap_data.linear.revmap;
if (unlikely(revmap == NULL))
return irq_find_mapping(host, hwirq);
/* Fill up revmap with slow path if no mapping found */
if (unlikely(revmap[hwirq] == NO_IRQ))
revmap[hwirq] = irq_find_mapping(host, hwirq);
return revmap[hwirq];
}
unsigned int irq_alloc_virt(struct irq_host *host,
unsigned int count,
unsigned int hint)
{
unsigned long flags;
unsigned int i, j, found = NO_IRQ;
if (count == 0 || count > (irq_virq_count - NUM_ISA_INTERRUPTS))
return NO_IRQ;
raw_spin_lock_irqsave(&irq_big_lock, flags);
/* Use hint for 1 interrupt if any */
if (count == 1 && hint >= NUM_ISA_INTERRUPTS &&
hint < irq_virq_count && irq_map[hint].host == NULL) {
found = hint;
goto hint_found;
}
/* Look for count consecutive numbers in the allocatable
* (non-legacy) space
*/
for (i = NUM_ISA_INTERRUPTS, j = 0; i < irq_virq_count; i++) {
if (irq_map[i].host != NULL)
j = 0;
else
j++;
if (j == count) {
found = i - count + 1;
break;
}
}
if (found == NO_IRQ) {
raw_spin_unlock_irqrestore(&irq_big_lock, flags);
return NO_IRQ;
}
hint_found:
for (i = found; i < (found + count); i++) {
irq_map[i].hwirq = host->inval_irq;
smp_wmb();
irq_map[i].host = host;
}
raw_spin_unlock_irqrestore(&irq_big_lock, flags);
return found;
}
void irq_free_virt(unsigned int virq, unsigned int count)
{
unsigned long flags;
unsigned int i;
WARN_ON (virq < NUM_ISA_INTERRUPTS);
WARN_ON (count == 0 || (virq + count) > irq_virq_count);
if (virq < NUM_ISA_INTERRUPTS) {
if (virq + count < NUM_ISA_INTERRUPTS)
return;
count =- NUM_ISA_INTERRUPTS - virq;
virq = NUM_ISA_INTERRUPTS;
}
if (count > irq_virq_count || virq > irq_virq_count - count) {
if (virq > irq_virq_count)
return;
count = irq_virq_count - virq;
}
raw_spin_lock_irqsave(&irq_big_lock, flags);
for (i = virq; i < (virq + count); i++) {
struct irq_host *host;
host = irq_map[i].host;
irq_map[i].hwirq = host->inval_irq;
smp_wmb();
irq_map[i].host = NULL;
}
raw_spin_unlock_irqrestore(&irq_big_lock, flags);
}
int arch_early_irq_init(void)
{
return 0;
}
#ifdef CONFIG_VIRQ_DEBUG
static int virq_debug_show(struct seq_file *m, void *private)
{
unsigned long flags;
struct irq_desc *desc;
const char *p;
static const char none[] = "none";
void *data;
int i;
seq_printf(m, "%-5s %-7s %-15s %-18s %s\n", "virq", "hwirq",
"chip name", "chip data", "host name");
for (i = 1; i < nr_irqs; i++) {
desc = irq_to_desc(i);
if (!desc)
continue;
raw_spin_lock_irqsave(&desc->lock, flags);
if (desc->action && desc->action->handler) {
struct irq_chip *chip;
seq_printf(m, "%5d ", i);
seq_printf(m, "0x%05lx ", irq_map[i].hwirq);
chip = irq_desc_get_chip(desc);
if (chip && chip->name)
p = chip->name;
else
p = none;
seq_printf(m, "%-15s ", p);
data = irq_desc_get_chip_data(desc);
seq_printf(m, "0x%16p ", data);
if (irq_map[i].host && irq_map[i].host->of_node)
p = irq_map[i].host->of_node->full_name;
else
p = none;
seq_printf(m, "%s\n", p);
}
raw_spin_unlock_irqrestore(&desc->lock, flags);
}
return 0;
}
static int virq_debug_open(struct inode *inode, struct file *file)
{
return single_open(file, virq_debug_show, inode->i_private);
}
static const struct file_operations virq_debug_fops = {
.open = virq_debug_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int __init irq_debugfs_init(void)
{
if (debugfs_create_file("virq_mapping", S_IRUGO, powerpc_debugfs_root,
NULL, &virq_debug_fops) == NULL)
return -ENOMEM;
return 0;
}
__initcall(irq_debugfs_init);
#endif /* CONFIG_VIRQ_DEBUG */
#ifdef CONFIG_PPC64
static int __init setup_noirqdistrib(char *str)
{
......
......@@ -21,7 +21,7 @@
#include <asm/prom.h>
static struct device_node *cpld_pic_node;
static struct irq_host *cpld_pic_host;
static struct irq_domain *cpld_pic_host;
/*
* Bits to ignore in the misc_status register
......@@ -123,13 +123,13 @@ cpld_pic_cascade(unsigned int irq, struct irq_desc *desc)
}
static int
cpld_pic_host_match(struct irq_host *h, struct device_node *node)
cpld_pic_host_match(struct irq_domain *h, struct device_node *node)
{
return cpld_pic_node == node;
}
static int
cpld_pic_host_map(struct irq_host *h, unsigned int virq,
cpld_pic_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
irq_set_status_flags(virq, IRQ_LEVEL);
......@@ -137,8 +137,7 @@ cpld_pic_host_map(struct irq_host *h, unsigned int virq,
return 0;
}
static struct
irq_host_ops cpld_pic_host_ops = {
static const struct irq_domain_ops cpld_pic_host_ops = {
.match = cpld_pic_host_match,
.map = cpld_pic_host_map,
};
......@@ -191,8 +190,7 @@ mpc5121_ads_cpld_pic_init(void)
cpld_pic_node = of_node_get(np);
cpld_pic_host =
irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, 16, &cpld_pic_host_ops, 16);
cpld_pic_host = irq_domain_add_linear(np, 16, &cpld_pic_host_ops, NULL);
if (!cpld_pic_host) {
printk(KERN_ERR "CPLD PIC: failed to allocate irq host!\n");
goto end;
......
......@@ -45,7 +45,7 @@ static struct of_device_id mpc5200_gpio_ids[] __initdata = {
struct media5200_irq {
void __iomem *regs;
spinlock_t lock;
struct irq_host *irqhost;
struct irq_domain *irqhost;
};
struct media5200_irq media5200_irq;
......@@ -112,7 +112,7 @@ void media5200_irq_cascade(unsigned int virq, struct irq_desc *desc)
raw_spin_unlock(&desc->lock);
}
static int media5200_irq_map(struct irq_host *h, unsigned int virq,
static int media5200_irq_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
pr_debug("%s: h=%p, virq=%i, hwirq=%i\n", __func__, h, virq, (int)hw);
......@@ -122,7 +122,7 @@ static int media5200_irq_map(struct irq_host *h, unsigned int virq,
return 0;
}
static int media5200_irq_xlate(struct irq_host *h, struct device_node *ct,
static int media5200_irq_xlate(struct irq_domain *h, struct device_node *ct,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq,
unsigned int *out_flags)
......@@ -136,7 +136,7 @@ static int media5200_irq_xlate(struct irq_host *h, struct device_node *ct,
return 0;
}
static struct irq_host_ops media5200_irq_ops = {
static const struct irq_domain_ops media5200_irq_ops = {
.map = media5200_irq_map,
.xlate = media5200_irq_xlate,
};
......@@ -173,15 +173,12 @@ static void __init media5200_init_irq(void)
spin_lock_init(&media5200_irq.lock);
media5200_irq.irqhost = irq_alloc_host(fpga_np, IRQ_HOST_MAP_LINEAR,
MEDIA5200_NUM_IRQS,
&media5200_irq_ops, -1);
media5200_irq.irqhost = irq_domain_add_linear(fpga_np,
MEDIA5200_NUM_IRQS, &media5200_irq_ops, &media5200_irq);
if (!media5200_irq.irqhost)
goto out;
pr_debug("%s: allocated irqhost\n", __func__);
media5200_irq.irqhost->host_data = &media5200_irq;
irq_set_handler_data(cascade_virq, &media5200_irq);
irq_set_chained_handler(cascade_virq, media5200_irq_cascade);
......
......@@ -81,7 +81,7 @@ MODULE_LICENSE("GPL");
* @regs: virtual address of GPT registers
* @lock: spinlock to coordinate between different functions.
* @gc: gpio_chip instance structure; used when GPIO is enabled
* @irqhost: Pointer to irq_host instance; used when IRQ mode is supported
* @irqhost: Pointer to irq_domain instance; used when IRQ mode is supported
* @wdt_mode: only relevant for gpt0: bit 0 (MPC52xx_GPT_CAN_WDT) indicates
* if the gpt may be used as wdt, bit 1 (MPC52xx_GPT_IS_WDT) indicates
* if the timer is actively used as wdt which blocks gpt functions
......@@ -91,7 +91,7 @@ struct mpc52xx_gpt_priv {
struct device *dev;
struct mpc52xx_gpt __iomem *regs;
spinlock_t lock;
struct irq_host *irqhost;
struct irq_domain *irqhost;
u32 ipb_freq;
u8 wdt_mode;
......@@ -204,7 +204,7 @@ void mpc52xx_gpt_irq_cascade(unsigned int virq, struct irq_desc *desc)
}
}
static int mpc52xx_gpt_irq_map(struct irq_host *h, unsigned int virq,
static int mpc52xx_gpt_irq_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
struct mpc52xx_gpt_priv *gpt = h->host_data;
......@@ -216,7 +216,7 @@ static int mpc52xx_gpt_irq_map(struct irq_host *h, unsigned int virq,
return 0;
}
static int mpc52xx_gpt_irq_xlate(struct irq_host *h, struct device_node *ct,
static int mpc52xx_gpt_irq_xlate(struct irq_domain *h, struct device_node *ct,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq,
unsigned int *out_flags)
......@@ -236,7 +236,7 @@ static int mpc52xx_gpt_irq_xlate(struct irq_host *h, struct device_node *ct,
return 0;
}
static struct irq_host_ops mpc52xx_gpt_irq_ops = {
static const struct irq_domain_ops mpc52xx_gpt_irq_ops = {
.map = mpc52xx_gpt_irq_map,
.xlate = mpc52xx_gpt_irq_xlate,
};
......@@ -252,14 +252,12 @@ mpc52xx_gpt_irq_setup(struct mpc52xx_gpt_priv *gpt, struct device_node *node)
if (!cascade_virq)
return;
gpt->irqhost = irq_alloc_host(node, IRQ_HOST_MAP_LINEAR, 1,
&mpc52xx_gpt_irq_ops, -1);
gpt->irqhost = irq_domain_add_linear(node, 1, &mpc52xx_gpt_irq_ops, gpt);
if (!gpt->irqhost) {
dev_err(gpt->dev, "irq_alloc_host() failed\n");
dev_err(gpt->dev, "irq_domain_add_linear() failed\n");
return;
}
gpt->irqhost->host_data = gpt;
irq_set_handler_data(cascade_virq, gpt);
irq_set_chained_handler(cascade_virq, mpc52xx_gpt_irq_cascade);
......
......@@ -132,7 +132,7 @@ static struct of_device_id mpc52xx_sdma_ids[] __initdata = {
static struct mpc52xx_intr __iomem *intr;
static struct mpc52xx_sdma __iomem *sdma;
static struct irq_host *mpc52xx_irqhost = NULL;
static struct irq_domain *mpc52xx_irqhost = NULL;
static unsigned char mpc52xx_map_senses[4] = {
IRQ_TYPE_LEVEL_HIGH,
......@@ -301,7 +301,7 @@ static int mpc52xx_is_extirq(int l1, int l2)
/**
* mpc52xx_irqhost_xlate - translate virq# from device tree interrupts property
*/
static int mpc52xx_irqhost_xlate(struct irq_host *h, struct device_node *ct,
static int mpc52xx_irqhost_xlate(struct irq_domain *h, struct device_node *ct,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq,
unsigned int *out_flags)
......@@ -335,7 +335,7 @@ static int mpc52xx_irqhost_xlate(struct irq_host *h, struct device_node *ct,
/**
* mpc52xx_irqhost_map - Hook to map from virq to an irq_chip structure
*/
static int mpc52xx_irqhost_map(struct irq_host *h, unsigned int virq,
static int mpc52xx_irqhost_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t irq)
{
int l1irq;
......@@ -384,7 +384,7 @@ static int mpc52xx_irqhost_map(struct irq_host *h, unsigned int virq,
return 0;
}
static struct irq_host_ops mpc52xx_irqhost_ops = {
static const struct irq_domain_ops mpc52xx_irqhost_ops = {
.xlate = mpc52xx_irqhost_xlate,
.map = mpc52xx_irqhost_map,
};
......@@ -444,9 +444,9 @@ void __init mpc52xx_init_irq(void)
* As last step, add an irq host to translate the real
* hw irq information provided by the ofw to linux virq
*/
mpc52xx_irqhost = irq_alloc_host(picnode, IRQ_HOST_MAP_LINEAR,
mpc52xx_irqhost = irq_domain_add_linear(picnode,
MPC52xx_IRQ_HIGHTESTHWIRQ,
&mpc52xx_irqhost_ops, -1);
&mpc52xx_irqhost_ops, NULL);
if (!mpc52xx_irqhost)
panic(__FILE__ ": Cannot allocate the IRQ host\n");
......
......@@ -29,7 +29,7 @@ static DEFINE_RAW_SPINLOCK(pci_pic_lock);
struct pq2ads_pci_pic {
struct device_node *node;
struct irq_host *host;
struct irq_domain *host;
struct {
u32 stat;
......@@ -103,7 +103,7 @@ static void pq2ads_pci_irq_demux(unsigned int irq, struct irq_desc *desc)
}
}
static int pci_pic_host_map(struct irq_host *h, unsigned int virq,
static int pci_pic_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
irq_set_status_flags(virq, IRQ_LEVEL);
......@@ -112,14 +112,14 @@ static int pci_pic_host_map(struct irq_host *h, unsigned int virq,
return 0;
}
static struct irq_host_ops pci_pic_host_ops = {
static const struct irq_domain_ops pci_pic_host_ops = {
.map = pci_pic_host_map,
};
int __init pq2ads_pci_init_irq(void)
{
struct pq2ads_pci_pic *priv;
struct irq_host *host;
struct irq_domain *host;
struct device_node *np;
int ret = -ENODEV;
int irq;
......@@ -156,17 +156,13 @@ int __init pq2ads_pci_init_irq(void)
out_be32(&priv->regs->mask, ~0);
mb();
host = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, NUM_IRQS,
&pci_pic_host_ops, NUM_IRQS);
host = irq_domain_add_linear(np, NUM_IRQS, &pci_pic_host_ops, priv);
if (!host) {
ret = -ENOMEM;
goto out_unmap_regs;
}
host->host_data = priv;
priv->host = host;
host->host_data = priv;
irq_set_handler_data(irq, priv);
irq_set_chained_handler(irq, pq2ads_pci_irq_demux);
......
......@@ -51,7 +51,7 @@ static struct socrates_fpga_irq_info fpga_irqs[SOCRATES_FPGA_NUM_IRQS] = {
static DEFINE_RAW_SPINLOCK(socrates_fpga_pic_lock);
static void __iomem *socrates_fpga_pic_iobase;
static struct irq_host *socrates_fpga_pic_irq_host;
static struct irq_domain *socrates_fpga_pic_irq_host;
static unsigned int socrates_fpga_irqs[3];
static inline uint32_t socrates_fpga_pic_read(int reg)
......@@ -227,7 +227,7 @@ static struct irq_chip socrates_fpga_pic_chip = {
.irq_set_type = socrates_fpga_pic_set_type,
};
static int socrates_fpga_pic_host_map(struct irq_host *h, unsigned int virq,
static int socrates_fpga_pic_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hwirq)
{
/* All interrupts are LEVEL sensitive */
......@@ -238,7 +238,7 @@ static int socrates_fpga_pic_host_map(struct irq_host *h, unsigned int virq,
return 0;
}
static int socrates_fpga_pic_host_xlate(struct irq_host *h,
static int socrates_fpga_pic_host_xlate(struct irq_domain *h,
struct device_node *ct, const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_flags)
{
......@@ -269,7 +269,7 @@ static int socrates_fpga_pic_host_xlate(struct irq_host *h,
return 0;
}
static struct irq_host_ops socrates_fpga_pic_host_ops = {
static const struct irq_domain_ops socrates_fpga_pic_host_ops = {
.map = socrates_fpga_pic_host_map,
.xlate = socrates_fpga_pic_host_xlate,
};
......@@ -279,10 +279,9 @@ void socrates_fpga_pic_init(struct device_node *pic)
unsigned long flags;
int i;
/* Setup an irq_host structure */
socrates_fpga_pic_irq_host = irq_alloc_host(pic, IRQ_HOST_MAP_LINEAR,
SOCRATES_FPGA_NUM_IRQS, &socrates_fpga_pic_host_ops,
SOCRATES_FPGA_NUM_IRQS);
/* Setup an irq_domain structure */
socrates_fpga_pic_irq_host = irq_domain_add_linear(pic,
SOCRATES_FPGA_NUM_IRQS, &socrates_fpga_pic_host_ops, NULL);
if (socrates_fpga_pic_irq_host == NULL) {
pr_err("FPGA PIC: Unable to allocate host\n");
return;
......
......@@ -50,7 +50,7 @@
static DEFINE_RAW_SPINLOCK(gef_pic_lock);
static void __iomem *gef_pic_irq_reg_base;
static struct irq_host *gef_pic_irq_host;
static struct irq_domain *gef_pic_irq_host;
static int gef_pic_cascade_irq;
/*
......@@ -153,7 +153,7 @@ static struct irq_chip gef_pic_chip = {
/* When an interrupt is being configured, this call allows some flexibilty
* in deciding which irq_chip structure is used
*/
static int gef_pic_host_map(struct irq_host *h, unsigned int virq,
static int gef_pic_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hwirq)
{
/* All interrupts are LEVEL sensitive */
......@@ -163,7 +163,7 @@ static int gef_pic_host_map(struct irq_host *h, unsigned int virq,
return 0;
}
static int gef_pic_host_xlate(struct irq_host *h, struct device_node *ct,
static int gef_pic_host_xlate(struct irq_domain *h, struct device_node *ct,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_flags)
{
......@@ -177,7 +177,7 @@ static int gef_pic_host_xlate(struct irq_host *h, struct device_node *ct,
return 0;
}
static struct irq_host_ops gef_pic_host_ops = {
static const struct irq_domain_ops gef_pic_host_ops = {
.map = gef_pic_host_map,
.xlate = gef_pic_host_xlate,
};
......@@ -211,10 +211,9 @@ void __init gef_pic_init(struct device_node *np)
return;
}
/* Setup an irq_host structure */
gef_pic_irq_host = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR,
GEF_PIC_NUM_IRQS,
&gef_pic_host_ops, NO_IRQ);
/* Setup an irq_domain structure */
gef_pic_irq_host = irq_domain_add_linear(np, GEF_PIC_NUM_IRQS,
&gef_pic_host_ops, NULL);
if (gef_pic_irq_host == NULL)
return;
......
......@@ -67,7 +67,7 @@
struct axon_msic {
struct irq_host *irq_host;
struct irq_domain *irq_domain;
__le32 *fifo_virt;
dma_addr_t fifo_phys;
dcr_host_t dcr_host;
......@@ -152,7 +152,7 @@ static void axon_msi_cascade(unsigned int irq, struct irq_desc *desc)
static struct axon_msic *find_msi_translator(struct pci_dev *dev)
{
struct irq_host *irq_host;
struct irq_domain *irq_domain;
struct device_node *dn, *tmp;
const phandle *ph;
struct axon_msic *msic = NULL;
......@@ -184,14 +184,14 @@ static struct axon_msic *find_msi_translator(struct pci_dev *dev)
goto out_error;
}
irq_host = irq_find_host(dn);
if (!irq_host) {
dev_dbg(&dev->dev, "axon_msi: no irq_host found for node %s\n",
irq_domain = irq_find_host(dn);
if (!irq_domain) {
dev_dbg(&dev->dev, "axon_msi: no irq_domain found for node %s\n",
dn->full_name);
goto out_error;
}
msic = irq_host->host_data;
msic = irq_domain->host_data;
out_error:
of_node_put(dn);
......@@ -280,7 +280,7 @@ static int axon_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
BUILD_BUG_ON(NR_IRQS > 65536);
list_for_each_entry(entry, &dev->msi_list, list) {
virq = irq_create_direct_mapping(msic->irq_host);
virq = irq_create_direct_mapping(msic->irq_domain);
if (virq == NO_IRQ) {
dev_warn(&dev->dev,
"axon_msi: virq allocation failed!\n");
......@@ -318,7 +318,7 @@ static struct irq_chip msic_irq_chip = {
.name = "AXON-MSI",
};
static int msic_host_map(struct irq_host *h, unsigned int virq,
static int msic_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
irq_set_chip_data(virq, h->host_data);
......@@ -327,7 +327,7 @@ static int msic_host_map(struct irq_host *h, unsigned int virq,
return 0;
}
static struct irq_host_ops msic_host_ops = {
static const struct irq_domain_ops msic_host_ops = {
.map = msic_host_map,
};
......@@ -337,7 +337,7 @@ static void axon_msi_shutdown(struct platform_device *device)
u32 tmp;
pr_devel("axon_msi: disabling %s\n",
msic->irq_host->of_node->full_name);
msic->irq_domain->of_node->full_name);
tmp = dcr_read(msic->dcr_host, MSIC_CTRL_REG);
tmp &= ~MSIC_CTRL_ENABLE & ~MSIC_CTRL_IRQ_ENABLE;
msic_dcr_write(msic, MSIC_CTRL_REG, tmp);
......@@ -392,16 +392,13 @@ static int axon_msi_probe(struct platform_device *device)
}
memset(msic->fifo_virt, 0xff, MSIC_FIFO_SIZE_BYTES);
msic->irq_host = irq_alloc_host(dn, IRQ_HOST_MAP_NOMAP,
NR_IRQS, &msic_host_ops, 0);
if (!msic->irq_host) {
printk(KERN_ERR "axon_msi: couldn't allocate irq_host for %s\n",
msic->irq_domain = irq_domain_add_nomap(dn, &msic_host_ops, msic);
if (!msic->irq_domain) {
printk(KERN_ERR "axon_msi: couldn't allocate irq_domain for %s\n",
dn->full_name);
goto out_free_fifo;
}
msic->irq_host->host_data = msic;
irq_set_handler_data(virq, msic);
irq_set_chained_handler(virq, axon_msi_cascade);
pr_devel("axon_msi: irq 0x%x setup for axon_msi\n", virq);
......
......@@ -34,7 +34,7 @@ static DEFINE_RAW_SPINLOCK(beatic_irq_mask_lock);
static uint64_t beatic_irq_mask_enable[(MAX_IRQS+255)/64];
static uint64_t beatic_irq_mask_ack[(MAX_IRQS+255)/64];
static struct irq_host *beatic_host;
static struct irq_domain *beatic_host;
/*
* In this implementation, "virq" == "IRQ plug number",
......@@ -122,7 +122,7 @@ static struct irq_chip beatic_pic = {
*
* Note that the number (virq) is already assigned at upper layer.
*/
static void beatic_pic_host_unmap(struct irq_host *h, unsigned int virq)
static void beatic_pic_host_unmap(struct irq_domain *h, unsigned int virq)
{
beat_destruct_irq_plug(virq);
}
......@@ -133,7 +133,7 @@ static void beatic_pic_host_unmap(struct irq_host *h, unsigned int virq)
*
* Note that the number (virq) is already assigned at upper layer.
*/
static int beatic_pic_host_map(struct irq_host *h, unsigned int virq,
static int beatic_pic_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
int64_t err;
......@@ -154,7 +154,7 @@ static int beatic_pic_host_map(struct irq_host *h, unsigned int virq,
* Called from irq_create_of_mapping() only.
* Note: We have only 1 entry to translate.
*/
static int beatic_pic_host_xlate(struct irq_host *h, struct device_node *ct,
static int beatic_pic_host_xlate(struct irq_domain *h, struct device_node *ct,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq,
unsigned int *out_flags)
......@@ -166,13 +166,13 @@ static int beatic_pic_host_xlate(struct irq_host *h, struct device_node *ct,
return 0;
}
static int beatic_pic_host_match(struct irq_host *h, struct device_node *np)
static int beatic_pic_host_match(struct irq_domain *h, struct device_node *np)
{
/* Match all */
return 1;
}
static struct irq_host_ops beatic_pic_host_ops = {
static const struct irq_domain_ops beatic_pic_host_ops = {
.map = beatic_pic_host_map,
.unmap = beatic_pic_host_unmap,
.xlate = beatic_pic_host_xlate,
......@@ -239,9 +239,7 @@ void __init beatic_init_IRQ(void)
ppc_md.get_irq = beatic_get_irq;
/* Allocate an irq host */
beatic_host = irq_alloc_host(NULL, IRQ_HOST_MAP_NOMAP, 0,
&beatic_pic_host_ops,
0);
beatic_host = irq_domain_add_nomap(NULL, &beatic_pic_host_ops, NULL);
BUG_ON(beatic_host == NULL);
irq_set_default_host(beatic_host);
}
......
......@@ -56,7 +56,7 @@ struct iic {
static DEFINE_PER_CPU(struct iic, cpu_iic);
#define IIC_NODE_COUNT 2
static struct irq_host *iic_host;
static struct irq_domain *iic_host;
/* Convert between "pending" bits and hw irq number */
static irq_hw_number_t iic_pending_to_hwnum(struct cbe_iic_pending_bits bits)
......@@ -186,7 +186,7 @@ void iic_message_pass(int cpu, int msg)
out_be64(&per_cpu(cpu_iic, cpu).regs->generate, (0xf - msg) << 4);
}
struct irq_host *iic_get_irq_host(int node)
struct irq_domain *iic_get_irq_host(int node)
{
return iic_host;
}
......@@ -222,13 +222,13 @@ void iic_request_IPIs(void)
#endif /* CONFIG_SMP */
static int iic_host_match(struct irq_host *h, struct device_node *node)
static int iic_host_match(struct irq_domain *h, struct device_node *node)
{
return of_device_is_compatible(node,
"IBM,CBEA-Internal-Interrupt-Controller");
}
static int iic_host_map(struct irq_host *h, unsigned int virq,
static int iic_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
switch (hw & IIC_IRQ_TYPE_MASK) {
......@@ -245,7 +245,7 @@ static int iic_host_map(struct irq_host *h, unsigned int virq,
return 0;
}
static int iic_host_xlate(struct irq_host *h, struct device_node *ct,
static int iic_host_xlate(struct irq_domain *h, struct device_node *ct,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_flags)
......@@ -285,7 +285,7 @@ static int iic_host_xlate(struct irq_host *h, struct device_node *ct,
return 0;
}
static struct irq_host_ops iic_host_ops = {
static const struct irq_domain_ops iic_host_ops = {
.match = iic_host_match,
.map = iic_host_map,
.xlate = iic_host_xlate,
......@@ -378,8 +378,8 @@ static int __init setup_iic(void)
void __init iic_init_IRQ(void)
{
/* Setup an irq host data structure */
iic_host = irq_alloc_host(NULL, IRQ_HOST_MAP_LINEAR, IIC_SOURCE_COUNT,
&iic_host_ops, IIC_IRQ_INVALID);
iic_host = irq_domain_add_linear(NULL, IIC_SOURCE_COUNT, &iic_host_ops,
NULL);
BUG_ON(iic_host == NULL);
irq_set_default_host(iic_host);
......
......@@ -62,7 +62,7 @@ enum {
#define SPIDER_IRQ_INVALID 63
struct spider_pic {
struct irq_host *host;
struct irq_domain *host;
void __iomem *regs;
unsigned int node_id;
};
......@@ -168,7 +168,7 @@ static struct irq_chip spider_pic = {
.irq_set_type = spider_set_irq_type,
};
static int spider_host_map(struct irq_host *h, unsigned int virq,
static int spider_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
irq_set_chip_data(virq, h->host_data);
......@@ -180,7 +180,7 @@ static int spider_host_map(struct irq_host *h, unsigned int virq,
return 0;
}
static int spider_host_xlate(struct irq_host *h, struct device_node *ct,
static int spider_host_xlate(struct irq_domain *h, struct device_node *ct,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_flags)
......@@ -194,7 +194,7 @@ static int spider_host_xlate(struct irq_host *h, struct device_node *ct,
return 0;
}
static struct irq_host_ops spider_host_ops = {
static const struct irq_domain_ops spider_host_ops = {
.map = spider_host_map,
.xlate = spider_host_xlate,
};
......@@ -299,12 +299,10 @@ static void __init spider_init_one(struct device_node *of_node, int chip,
panic("spider_pic: can't map registers !");
/* Allocate a host */
pic->host = irq_alloc_host(of_node, IRQ_HOST_MAP_LINEAR,
SPIDER_SRC_COUNT, &spider_host_ops,
SPIDER_IRQ_INVALID);
pic->host = irq_domain_add_linear(of_node, SPIDER_SRC_COUNT,
&spider_host_ops, pic);
if (pic->host == NULL)
panic("spider_pic: can't allocate irq host !");
pic->host->host_data = pic;
/* Go through all sources and disable them */
for (i = 0; i < SPIDER_SRC_COUNT; i++) {
......
......@@ -96,9 +96,9 @@ static struct irq_chip flipper_pic = {
*
*/
static struct irq_host *flipper_irq_host;
static struct irq_domain *flipper_irq_host;
static int flipper_pic_map(struct irq_host *h, unsigned int virq,
static int flipper_pic_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hwirq)
{
irq_set_chip_data(virq, h->host_data);
......@@ -107,13 +107,13 @@ static int flipper_pic_map(struct irq_host *h, unsigned int virq,
return 0;
}
static int flipper_pic_match(struct irq_host *h, struct device_node *np)
static int flipper_pic_match(struct irq_domain *h, struct device_node *np)
{
return 1;
}
static struct irq_host_ops flipper_irq_host_ops = {
static const struct irq_domain_ops flipper_irq_domain_ops = {
.map = flipper_pic_map,
.match = flipper_pic_match,
};
......@@ -130,10 +130,10 @@ static void __flipper_quiesce(void __iomem *io_base)
out_be32(io_base + FLIPPER_ICR, 0xffffffff);
}
struct irq_host * __init flipper_pic_init(struct device_node *np)
struct irq_domain * __init flipper_pic_init(struct device_node *np)
{
struct device_node *pi;
struct irq_host *irq_host = NULL;
struct irq_domain *irq_domain = NULL;
struct resource res;
void __iomem *io_base;
int retval;
......@@ -159,17 +159,15 @@ struct irq_host * __init flipper_pic_init(struct device_node *np)
__flipper_quiesce(io_base);
irq_host = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, FLIPPER_NR_IRQS,
&flipper_irq_host_ops, -1);
if (!irq_host) {
pr_err("failed to allocate irq_host\n");
irq_domain = irq_domain_add_linear(np, FLIPPER_NR_IRQS,
&flipper_irq_domain_ops, io_base);
if (!irq_domain) {
pr_err("failed to allocate irq_domain\n");
return NULL;
}
irq_host->host_data = io_base;
out:
return irq_host;
return irq_domain;
}
unsigned int flipper_pic_get_irq(void)
......
......@@ -89,9 +89,9 @@ static struct irq_chip hlwd_pic = {
*
*/
static struct irq_host *hlwd_irq_host;
static struct irq_domain *hlwd_irq_host;
static int hlwd_pic_map(struct irq_host *h, unsigned int virq,
static int hlwd_pic_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hwirq)
{
irq_set_chip_data(virq, h->host_data);
......@@ -100,11 +100,11 @@ static int hlwd_pic_map(struct irq_host *h, unsigned int virq,
return 0;
}
static struct irq_host_ops hlwd_irq_host_ops = {
static const struct irq_domain_ops hlwd_irq_domain_ops = {
.map = hlwd_pic_map,
};
static unsigned int __hlwd_pic_get_irq(struct irq_host *h)
static unsigned int __hlwd_pic_get_irq(struct irq_domain *h)
{
void __iomem *io_base = h->host_data;
int irq;
......@@ -123,14 +123,14 @@ static void hlwd_pic_irq_cascade(unsigned int cascade_virq,
struct irq_desc *desc)
{
struct irq_chip *chip = irq_desc_get_chip(desc);
struct irq_host *irq_host = irq_get_handler_data(cascade_virq);
struct irq_domain *irq_domain = irq_get_handler_data(cascade_virq);
unsigned int virq;
raw_spin_lock(&desc->lock);
chip->irq_mask(&desc->irq_data); /* IRQ_LEVEL */
raw_spin_unlock(&desc->lock);
virq = __hlwd_pic_get_irq(irq_host);
virq = __hlwd_pic_get_irq(irq_domain);
if (virq != NO_IRQ)
generic_handle_irq(virq);
else
......@@ -155,9 +155,9 @@ static void __hlwd_quiesce(void __iomem *io_base)
out_be32(io_base + HW_BROADWAY_ICR, 0xffffffff);
}
struct irq_host *hlwd_pic_init(struct device_node *np)
struct irq_domain *hlwd_pic_init(struct device_node *np)
{
struct irq_host *irq_host;
struct irq_domain *irq_domain;
struct resource res;
void __iomem *io_base;
int retval;
......@@ -177,15 +177,14 @@ struct irq_host *hlwd_pic_init(struct device_node *np)
__hlwd_quiesce(io_base);
irq_host = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, HLWD_NR_IRQS,
&hlwd_irq_host_ops, -1);
if (!irq_host) {
pr_err("failed to allocate irq_host\n");
irq_domain = irq_domain_add_linear(np, HLWD_NR_IRQS,
&hlwd_irq_domain_ops, io_base);
if (!irq_domain) {
pr_err("failed to allocate irq_domain\n");
return NULL;
}
irq_host->host_data = io_base;
return irq_host;
return irq_domain;
}
unsigned int hlwd_pic_get_irq(void)
......@@ -200,7 +199,7 @@ unsigned int hlwd_pic_get_irq(void)
void hlwd_pic_probe(void)
{
struct irq_host *host;
struct irq_domain *host;
struct device_node *np;
const u32 *interrupts;
int cascade_virq;
......
......@@ -342,7 +342,7 @@ unsigned int iSeries_get_irq(void)
#ifdef CONFIG_PCI
static int iseries_irq_host_map(struct irq_host *h, unsigned int virq,
static int iseries_irq_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
irq_set_chip_and_handler(virq, &iseries_pic, handle_fasteoi_irq);
......@@ -350,13 +350,13 @@ static int iseries_irq_host_map(struct irq_host *h, unsigned int virq,
return 0;
}
static int iseries_irq_host_match(struct irq_host *h, struct device_node *np)
static int iseries_irq_host_match(struct irq_domain *h, struct device_node *np)
{
/* Match all */
return 1;
}
static struct irq_host_ops iseries_irq_host_ops = {
static const struct irq_domain_ops iseries_irq_domain_ops = {
.map = iseries_irq_host_map,
.match = iseries_irq_host_match,
};
......@@ -368,7 +368,7 @@ static struct irq_host_ops iseries_irq_host_ops = {
void __init iSeries_init_IRQ(void)
{
/* Register PCI event handler and open an event path */
struct irq_host *host;
struct irq_domain *host;
int ret;
/*
......@@ -380,8 +380,7 @@ void __init iSeries_init_IRQ(void)
/* Create irq host. No need for a revmap since HV will give us
* back our virtual irq number
*/
host = irq_alloc_host(NULL, IRQ_HOST_MAP_NOMAP, 0,
&iseries_irq_host_ops, 0);
host = irq_domain_add_nomap(NULL, &iseries_irq_domain_ops, NULL);
BUG_ON(host == NULL);
irq_set_default_host(host);
......
......@@ -61,7 +61,7 @@ static DEFINE_RAW_SPINLOCK(pmac_pic_lock);
static unsigned long ppc_lost_interrupts[NR_MASK_WORDS];
static unsigned long ppc_cached_irq_mask[NR_MASK_WORDS];
static int pmac_irq_cascade = -1;
static struct irq_host *pmac_pic_host;
static struct irq_domain *pmac_pic_host;
static void __pmac_retrigger(unsigned int irq_nr)
{
......@@ -268,13 +268,13 @@ static struct irqaction gatwick_cascade_action = {
.name = "cascade",
};
static int pmac_pic_host_match(struct irq_host *h, struct device_node *node)
static int pmac_pic_host_match(struct irq_domain *h, struct device_node *node)
{
/* We match all, we don't always have a node anyway */
return 1;
}
static int pmac_pic_host_map(struct irq_host *h, unsigned int virq,
static int pmac_pic_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
if (hw >= max_irqs)
......@@ -288,21 +288,10 @@ static int pmac_pic_host_map(struct irq_host *h, unsigned int virq,
return 0;
}
static int pmac_pic_host_xlate(struct irq_host *h, struct device_node *ct,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq,
unsigned int *out_flags)
{
*out_flags = IRQ_TYPE_NONE;
*out_hwirq = *intspec;
return 0;
}
static struct irq_host_ops pmac_pic_host_ops = {
static const struct irq_domain_ops pmac_pic_host_ops = {
.match = pmac_pic_host_match,
.map = pmac_pic_host_map,
.xlate = pmac_pic_host_xlate,
.xlate = irq_domain_xlate_onecell,
};
static void __init pmac_pic_probe_oldstyle(void)
......@@ -352,9 +341,8 @@ static void __init pmac_pic_probe_oldstyle(void)
/*
* Allocate an irq host
*/
pmac_pic_host = irq_alloc_host(master, IRQ_HOST_MAP_LINEAR, max_irqs,
&pmac_pic_host_ops,
max_irqs);
pmac_pic_host = irq_domain_add_linear(master, max_irqs,
&pmac_pic_host_ops, NULL);
BUG_ON(pmac_pic_host == NULL);
irq_set_default_host(pmac_pic_host);
......
......@@ -125,7 +125,7 @@ static volatile u32 __iomem *psurge_start;
static int psurge_type = PSURGE_NONE;
/* irq for secondary cpus to report */
static struct irq_host *psurge_host;
static struct irq_domain *psurge_host;
int psurge_secondary_virq;
/*
......@@ -176,7 +176,7 @@ static void smp_psurge_cause_ipi(int cpu, unsigned long data)
psurge_set_ipi(cpu);
}
static int psurge_host_map(struct irq_host *h, unsigned int virq,
static int psurge_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
irq_set_chip_and_handler(virq, &dummy_irq_chip, handle_percpu_irq);
......@@ -184,7 +184,7 @@ static int psurge_host_map(struct irq_host *h, unsigned int virq,
return 0;
}
struct irq_host_ops psurge_host_ops = {
static const struct irq_domain_ops psurge_host_ops = {
.map = psurge_host_map,
};
......@@ -192,8 +192,7 @@ static int psurge_secondary_ipi_init(void)
{
int rc = -ENOMEM;
psurge_host = irq_alloc_host(NULL, IRQ_HOST_MAP_NOMAP, 0,
&psurge_host_ops, 0);
psurge_host = irq_domain_add_nomap(NULL, &psurge_host_ops, NULL);
if (psurge_host)
psurge_secondary_virq = irq_create_direct_mapping(psurge_host);
......
......@@ -667,7 +667,7 @@ static void __maybe_unused _dump_mask(struct ps3_private *pd,
static void dump_bmp(struct ps3_private* pd) {};
#endif /* defined(DEBUG) */
static int ps3_host_map(struct irq_host *h, unsigned int virq,
static int ps3_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hwirq)
{
DBG("%s:%d: hwirq %lu, virq %u\n", __func__, __LINE__, hwirq,
......@@ -678,13 +678,13 @@ static int ps3_host_map(struct irq_host *h, unsigned int virq,
return 0;
}
static int ps3_host_match(struct irq_host *h, struct device_node *np)
static int ps3_host_match(struct irq_domain *h, struct device_node *np)
{
/* Match all */
return 1;
}
static struct irq_host_ops ps3_host_ops = {
static const struct irq_domain_ops ps3_host_ops = {
.map = ps3_host_map,
.match = ps3_host_match,
};
......@@ -751,10 +751,9 @@ void __init ps3_init_IRQ(void)
{
int result;
unsigned cpu;
struct irq_host *host;
struct irq_domain *host;
host = irq_alloc_host(NULL, IRQ_HOST_MAP_NOMAP, 0, &ps3_host_ops,
PS3_INVALID_OUTLET);
host = irq_domain_add_nomap(NULL, &ps3_host_ops, NULL);
irq_set_default_host(host);
irq_set_virq_count(PS3_PLUG_MAX + 1);
......
......@@ -30,7 +30,7 @@
static int opb_index = 0;
struct opb_pic {
struct irq_host *host;
struct irq_domain *host;
void *regs;
int index;
spinlock_t lock;
......@@ -179,7 +179,7 @@ static struct irq_chip opb_irq_chip = {
.irq_set_type = opb_set_irq_type
};
static int opb_host_map(struct irq_host *host, unsigned int virq,
static int opb_host_map(struct irq_domain *host, unsigned int virq,
irq_hw_number_t hwirq)
{
struct opb_pic *opb;
......@@ -196,20 +196,9 @@ static int opb_host_map(struct irq_host *host, unsigned int virq,
return 0;
}
static int opb_host_xlate(struct irq_host *host, struct device_node *dn,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_type)
{
/* Interrupt size must == 2 */
BUG_ON(intsize != 2);
*out_hwirq = intspec[0];
*out_type = intspec[1];
return 0;
}
static struct irq_host_ops opb_host_ops = {
static const struct irq_domain_ops opb_host_ops = {
.map = opb_host_map,
.xlate = opb_host_xlate,
.xlate = irq_domain_xlate_twocell,
};
irqreturn_t opb_irq_handler(int irq, void *private)
......@@ -263,13 +252,11 @@ struct opb_pic *opb_pic_init_one(struct device_node *dn)
goto free_opb;
}
/* Allocate an irq host so that Linux knows that despite only
/* Allocate an irq domain so that Linux knows that despite only
* having one interrupt to issue, we're the controller for multiple
* hardware IRQs, so later we can lookup their virtual IRQs. */
opb->host = irq_alloc_host(dn, IRQ_HOST_MAP_LINEAR,
OPB_NR_IRQS, &opb_host_ops, -1);
opb->host = irq_domain_add_linear(dn, OPB_NR_IRQS, &opb_host_ops, opb);
if (!opb->host) {
printk(KERN_ERR "opb: Failed to allocate IRQ host!\n");
goto free_regs;
......@@ -277,7 +264,6 @@ struct opb_pic *opb_pic_init_one(struct device_node *dn)
opb->index = opb_index++;
spin_lock_init(&opb->lock);
opb->host->host_data = opb;
/* Disable all interrupts by default */
opb_out(opb, OPB_MLSASIER, 0);
......
......@@ -54,7 +54,7 @@ cpm8xx_t __iomem *cpmp; /* Pointer to comm processor space */
immap_t __iomem *mpc8xx_immr;
static cpic8xx_t __iomem *cpic_reg;
static struct irq_host *cpm_pic_host;
static struct irq_domain *cpm_pic_host;
static void cpm_mask_irq(struct irq_data *d)
{
......@@ -98,7 +98,7 @@ int cpm_get_irq(void)
return irq_linear_revmap(cpm_pic_host, cpm_vec);
}
static int cpm_pic_host_map(struct irq_host *h, unsigned int virq,
static int cpm_pic_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
pr_debug("cpm_pic_host_map(%d, 0x%lx)\n", virq, hw);
......@@ -123,7 +123,7 @@ static struct irqaction cpm_error_irqaction = {
.name = "error",
};
static struct irq_host_ops cpm_pic_host_ops = {
static const struct irq_domain_ops cpm_pic_host_ops = {
.map = cpm_pic_host_map,
};
......@@ -164,8 +164,7 @@ unsigned int cpm_pic_init(void)
out_be32(&cpic_reg->cpic_cimr, 0);
cpm_pic_host = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR,
64, &cpm_pic_host_ops, 64);
cpm_pic_host = irq_domain_add_linear(np, 64, &cpm_pic_host_ops, NULL);
if (cpm_pic_host == NULL) {
printk(KERN_ERR "CPM2 PIC: failed to allocate irq host!\n");
sirq = NO_IRQ;
......
......@@ -50,7 +50,7 @@
static intctl_cpm2_t __iomem *cpm2_intctl;
static struct irq_host *cpm2_pic_host;
static struct irq_domain *cpm2_pic_host;
#define NR_MASK_WORDS ((NR_IRQS + 31) / 32)
static unsigned long ppc_cached_irq_mask[NR_MASK_WORDS];
......@@ -214,7 +214,7 @@ unsigned int cpm2_get_irq(void)
return irq_linear_revmap(cpm2_pic_host, irq);
}
static int cpm2_pic_host_map(struct irq_host *h, unsigned int virq,
static int cpm2_pic_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
pr_debug("cpm2_pic_host_map(%d, 0x%lx)\n", virq, hw);
......@@ -224,21 +224,9 @@ static int cpm2_pic_host_map(struct irq_host *h, unsigned int virq,
return 0;
}
static int cpm2_pic_host_xlate(struct irq_host *h, struct device_node *ct,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_flags)
{
*out_hwirq = intspec[0];
if (intsize > 1)
*out_flags = intspec[1];
else
*out_flags = IRQ_TYPE_NONE;
return 0;
}
static struct irq_host_ops cpm2_pic_host_ops = {
static const struct irq_domain_ops cpm2_pic_host_ops = {
.map = cpm2_pic_host_map,
.xlate = cpm2_pic_host_xlate,
.xlate = irq_domain_xlate_onetwocell,
};
void cpm2_pic_init(struct device_node *node)
......@@ -275,8 +263,7 @@ void cpm2_pic_init(struct device_node *node)
out_be32(&cpm2_intctl->ic_scprrl, 0x05309770);
/* create a legacy host */
cpm2_pic_host = irq_alloc_host(node, IRQ_HOST_MAP_LINEAR,
64, &cpm2_pic_host_ops, 64);
cpm2_pic_host = irq_domain_add_linear(node, 64, &cpm2_pic_host_ops, NULL);
if (cpm2_pic_host == NULL) {
printk(KERN_ERR "CPM2 PIC: failed to allocate irq host!\n");
return;
......
......@@ -182,13 +182,13 @@ unsigned int ehv_pic_get_irq(void)
return irq_linear_revmap(global_ehv_pic->irqhost, irq);
}
static int ehv_pic_host_match(struct irq_host *h, struct device_node *node)
static int ehv_pic_host_match(struct irq_domain *h, struct device_node *node)
{
/* Exact match, unless ehv_pic node is NULL */
return h->of_node == NULL || h->of_node == node;
}
static int ehv_pic_host_map(struct irq_host *h, unsigned int virq,
static int ehv_pic_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
struct ehv_pic *ehv_pic = h->host_data;
......@@ -217,7 +217,7 @@ static int ehv_pic_host_map(struct irq_host *h, unsigned int virq,
return 0;
}
static int ehv_pic_host_xlate(struct irq_host *h, struct device_node *ct,
static int ehv_pic_host_xlate(struct irq_domain *h, struct device_node *ct,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_flags)
......@@ -248,7 +248,7 @@ static int ehv_pic_host_xlate(struct irq_host *h, struct device_node *ct,
return 0;
}
static struct irq_host_ops ehv_pic_host_ops = {
static const struct irq_domain_ops ehv_pic_host_ops = {
.match = ehv_pic_host_match,
.map = ehv_pic_host_map,
.xlate = ehv_pic_host_xlate,
......@@ -275,9 +275,8 @@ void __init ehv_pic_init(void)
return;
}
ehv_pic->irqhost = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR,
NR_EHV_PIC_INTS, &ehv_pic_host_ops, 0);
ehv_pic->irqhost = irq_domain_add_linear(np, NR_EHV_PIC_INTS,
&ehv_pic_host_ops, ehv_pic);
if (!ehv_pic->irqhost) {
of_node_put(np);
kfree(ehv_pic);
......@@ -293,7 +292,6 @@ void __init ehv_pic_init(void)
of_node_put(np2);
}
ehv_pic->irqhost->host_data = ehv_pic;
ehv_pic->hc_irq = ehv_pic_irq_chip;
ehv_pic->hc_irq.irq_set_affinity = ehv_pic_set_affinity;
ehv_pic->coreint_flag = coreint_flag;
......
......@@ -60,7 +60,7 @@ static struct irq_chip fsl_msi_chip = {
.name = "FSL-MSI",
};
static int fsl_msi_host_map(struct irq_host *h, unsigned int virq,
static int fsl_msi_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
struct fsl_msi *msi_data = h->host_data;
......@@ -74,7 +74,7 @@ static int fsl_msi_host_map(struct irq_host *h, unsigned int virq,
return 0;
}
static struct irq_host_ops fsl_msi_host_ops = {
static const struct irq_domain_ops fsl_msi_host_ops = {
.map = fsl_msi_host_map,
};
......@@ -387,8 +387,8 @@ static int __devinit fsl_of_msi_probe(struct platform_device *dev)
}
platform_set_drvdata(dev, msi);
msi->irqhost = irq_alloc_host(dev->dev.of_node, IRQ_HOST_MAP_LINEAR,
NR_MSI_IRQS, &fsl_msi_host_ops, 0);
msi->irqhost = irq_domain_add_linear(dev->dev.of_node,
NR_MSI_IRQS, &fsl_msi_host_ops, msi);
if (msi->irqhost == NULL) {
dev_err(&dev->dev, "No memory for MSI irqhost\n");
......@@ -420,8 +420,6 @@ static int __devinit fsl_of_msi_probe(struct platform_device *dev)
msi->feature = features->fsl_pic_ip;
msi->irqhost->host_data = msi;
/*
* Remember the phandle, so that we can match with any PCI nodes
* that have an "fsl,msi" property.
......
......@@ -26,7 +26,7 @@
#define FSL_PIC_IP_VMPIC 0x00000003
struct fsl_msi {
struct irq_host *irqhost;
struct irq_domain *irqhost;
unsigned long cascade_irq;
......
......@@ -25,7 +25,7 @@ static unsigned char cached_8259[2] = { 0xff, 0xff };
static DEFINE_RAW_SPINLOCK(i8259_lock);
static struct irq_host *i8259_host;
static struct irq_domain *i8259_host;
/*
* Acknowledge the IRQ using either the PCI host bridge's interrupt
......@@ -163,12 +163,12 @@ static struct resource pic_edgectrl_iores = {
.flags = IORESOURCE_BUSY,
};
static int i8259_host_match(struct irq_host *h, struct device_node *node)
static int i8259_host_match(struct irq_domain *h, struct device_node *node)
{
return h->of_node == NULL || h->of_node == node;
}
static int i8259_host_map(struct irq_host *h, unsigned int virq,
static int i8259_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
pr_debug("i8259_host_map(%d, 0x%lx)\n", virq, hw);
......@@ -185,7 +185,7 @@ static int i8259_host_map(struct irq_host *h, unsigned int virq,
return 0;
}
static int i8259_host_xlate(struct irq_host *h, struct device_node *ct,
static int i8259_host_xlate(struct irq_domain *h, struct device_node *ct,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_flags)
{
......@@ -205,13 +205,13 @@ static int i8259_host_xlate(struct irq_host *h, struct device_node *ct,
return 0;
}
static struct irq_host_ops i8259_host_ops = {
static struct irq_domain_ops i8259_host_ops = {
.match = i8259_host_match,
.map = i8259_host_map,
.xlate = i8259_host_xlate,
};
struct irq_host *i8259_get_host(void)
struct irq_domain *i8259_get_host(void)
{
return i8259_host;
}
......@@ -263,8 +263,7 @@ void i8259_init(struct device_node *node, unsigned long intack_addr)
raw_spin_unlock_irqrestore(&i8259_lock, flags);
/* create a legacy host */
i8259_host = irq_alloc_host(node, IRQ_HOST_MAP_LEGACY,
0, &i8259_host_ops, 0);
i8259_host = irq_domain_add_legacy_isa(node, &i8259_host_ops, NULL);
if (i8259_host == NULL) {
printk(KERN_ERR "i8259: failed to allocate irq host !\n");
return;
......
......@@ -672,13 +672,13 @@ static struct irq_chip ipic_edge_irq_chip = {
.irq_set_type = ipic_set_irq_type,
};
static int ipic_host_match(struct irq_host *h, struct device_node *node)
static int ipic_host_match(struct irq_domain *h, struct device_node *node)
{
/* Exact match, unless ipic node is NULL */
return h->of_node == NULL || h->of_node == node;
}
static int ipic_host_map(struct irq_host *h, unsigned int virq,
static int ipic_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
struct ipic *ipic = h->host_data;
......@@ -692,26 +692,10 @@ static int ipic_host_map(struct irq_host *h, unsigned int virq,
return 0;
}
static int ipic_host_xlate(struct irq_host *h, struct device_node *ct,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_flags)
{
/* interrupt sense values coming from the device tree equal either
* LEVEL_LOW (low assertion) or EDGE_FALLING (high-to-low change)
*/
*out_hwirq = intspec[0];
if (intsize > 1)
*out_flags = intspec[1];
else
*out_flags = IRQ_TYPE_NONE;
return 0;
}
static struct irq_host_ops ipic_host_ops = {
static struct irq_domain_ops ipic_host_ops = {
.match = ipic_host_match,
.map = ipic_host_map,
.xlate = ipic_host_xlate,
.xlate = irq_domain_xlate_onetwocell,
};
struct ipic * __init ipic_init(struct device_node *node, unsigned int flags)
......@@ -728,9 +712,8 @@ struct ipic * __init ipic_init(struct device_node *node, unsigned int flags)
if (ipic == NULL)
return NULL;
ipic->irqhost = irq_alloc_host(node, IRQ_HOST_MAP_LINEAR,
NR_IPIC_INTS,
&ipic_host_ops, 0);
ipic->irqhost = irq_domain_add_linear(node, NR_IPIC_INTS,
&ipic_host_ops, ipic);
if (ipic->irqhost == NULL) {
kfree(ipic);
return NULL;
......@@ -738,8 +721,6 @@ struct ipic * __init ipic_init(struct device_node *node, unsigned int flags)
ipic->regs = ioremap(res.start, resource_size(&res));
ipic->irqhost->host_data = ipic;
/* init hw */
ipic_write(ipic->regs, IPIC_SICNR, 0x0);
......
......@@ -43,7 +43,7 @@ struct ipic {
volatile u32 __iomem *regs;
/* The remapper for this IPIC */
struct irq_host *irqhost;
struct irq_domain *irqhost;
};
struct ipic_info {
......
......@@ -17,7 +17,7 @@
extern int cpm_get_irq(struct pt_regs *regs);
static struct irq_host *mpc8xx_pic_host;
static struct irq_domain *mpc8xx_pic_host;
#define NR_MASK_WORDS ((NR_IRQS + 31) / 32)
static unsigned long ppc_cached_irq_mask[NR_MASK_WORDS];
static sysconf8xx_t __iomem *siu_reg;
......@@ -110,7 +110,7 @@ unsigned int mpc8xx_get_irq(void)
}
static int mpc8xx_pic_host_map(struct irq_host *h, unsigned int virq,
static int mpc8xx_pic_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
pr_debug("mpc8xx_pic_host_map(%d, 0x%lx)\n", virq, hw);
......@@ -121,7 +121,7 @@ static int mpc8xx_pic_host_map(struct irq_host *h, unsigned int virq,
}
static int mpc8xx_pic_host_xlate(struct irq_host *h, struct device_node *ct,
static int mpc8xx_pic_host_xlate(struct irq_domain *h, struct device_node *ct,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_flags)
{
......@@ -142,7 +142,7 @@ static int mpc8xx_pic_host_xlate(struct irq_host *h, struct device_node *ct,
}
static struct irq_host_ops mpc8xx_pic_host_ops = {
static struct irq_domain_ops mpc8xx_pic_host_ops = {
.map = mpc8xx_pic_host_map,
.xlate = mpc8xx_pic_host_xlate,
};
......@@ -171,8 +171,7 @@ int mpc8xx_pic_init(void)
goto out;
}
mpc8xx_pic_host = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR,
64, &mpc8xx_pic_host_ops, 64);
mpc8xx_pic_host = irq_domain_add_linear(np, 64, &mpc8xx_pic_host_ops, NULL);
if (mpc8xx_pic_host == NULL) {
printk(KERN_ERR "MPC8xx PIC: failed to allocate irq host!\n");
ret = -ENOMEM;
......
......@@ -965,13 +965,13 @@ static struct irq_chip mpic_irq_ht_chip = {
#endif /* CONFIG_MPIC_U3_HT_IRQS */
static int mpic_host_match(struct irq_host *h, struct device_node *node)
static int mpic_host_match(struct irq_domain *h, struct device_node *node)
{
/* Exact match, unless mpic node is NULL */
return h->of_node == NULL || h->of_node == node;
}
static int mpic_host_map(struct irq_host *h, unsigned int virq,
static int mpic_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
struct mpic *mpic = h->host_data;
......@@ -1041,7 +1041,7 @@ static int mpic_host_map(struct irq_host *h, unsigned int virq,
return 0;
}
static int mpic_host_xlate(struct irq_host *h, struct device_node *ct,
static int mpic_host_xlate(struct irq_domain *h, struct device_node *ct,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_flags)
......@@ -1121,13 +1121,13 @@ static void mpic_cascade(unsigned int irq, struct irq_desc *desc)
BUG_ON(!(mpic->flags & MPIC_SECONDARY));
virq = mpic_get_one_irq(mpic);
if (virq != NO_IRQ)
if (virq)
generic_handle_irq(virq);
chip->irq_eoi(&desc->irq_data);
}
static struct irq_host_ops mpic_host_ops = {
static struct irq_domain_ops mpic_host_ops = {
.match = mpic_host_match,
.map = mpic_host_map,
.xlate = mpic_host_xlate,
......@@ -1345,10 +1345,9 @@ struct mpic * __init mpic_alloc(struct device_node *node,
mpic->isu_shift = 1 + __ilog2(mpic->isu_size - 1);
mpic->isu_mask = (1 << mpic->isu_shift) - 1;
mpic->irqhost = irq_alloc_host(mpic->node, IRQ_HOST_MAP_LINEAR,
mpic->irqhost = irq_domain_add_linear(mpic->node,
isu_size ? isu_size : mpic->num_sources,
&mpic_host_ops,
flags & MPIC_LARGE_VECTORS ? 2048 : 256);
&mpic_host_ops, mpic);
/*
* FIXME: The code leaks the MPIC object and mappings here; this
......@@ -1357,8 +1356,6 @@ struct mpic * __init mpic_alloc(struct device_node *node,
if (mpic->irqhost == NULL)
return NULL;
mpic->irqhost->host_data = mpic;
/* Display version */
switch (greg_feature & MPIC_GREG_FEATURE_VERSION_MASK) {
case 1:
......
......@@ -32,7 +32,7 @@ void mpic_msi_reserve_hwirq(struct mpic *mpic, irq_hw_number_t hwirq)
static int mpic_msi_reserve_u3_hwirqs(struct mpic *mpic)
{
irq_hw_number_t hwirq;
struct irq_host_ops *ops = mpic->irqhost->ops;
const struct irq_domain_ops *ops = mpic->irqhost->ops;
struct device_node *np;
int flags, index, i;
struct of_irq oirq;
......
......@@ -70,7 +70,7 @@ static u32 mv64x60_cached_low_mask;
static u32 mv64x60_cached_high_mask = MV64X60_HIGH_GPP_GROUPS;
static u32 mv64x60_cached_gpp_mask;
static struct irq_host *mv64x60_irq_host;
static struct irq_domain *mv64x60_irq_host;
/*
* mv64x60_chip_low functions
......@@ -208,7 +208,7 @@ static struct irq_chip *mv64x60_chips[] = {
[MV64x60_LEVEL1_GPP] = &mv64x60_chip_gpp,
};
static int mv64x60_host_map(struct irq_host *h, unsigned int virq,
static int mv64x60_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hwirq)
{
int level1;
......@@ -223,7 +223,7 @@ static int mv64x60_host_map(struct irq_host *h, unsigned int virq,
return 0;
}
static struct irq_host_ops mv64x60_host_ops = {
static struct irq_domain_ops mv64x60_host_ops = {
.map = mv64x60_host_map,
};
......@@ -250,9 +250,8 @@ void __init mv64x60_init_irq(void)
paddr = of_translate_address(np, reg);
mv64x60_irq_reg_base = ioremap(paddr, reg[1]);
mv64x60_irq_host = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR,
MV64x60_NUM_IRQS,
&mv64x60_host_ops, MV64x60_NUM_IRQS);
mv64x60_irq_host = irq_domain_add_linear(np, MV64x60_NUM_IRQS,
&mv64x60_host_ops, NULL);
spin_lock_irqsave(&mv64x60_lock, flags);
out_le32(mv64x60_gpp_reg_base + MV64x60_GPP_INTR_MASK,
......
......@@ -245,13 +245,13 @@ static struct irq_chip qe_ic_irq_chip = {
.irq_mask_ack = qe_ic_mask_irq,
};
static int qe_ic_host_match(struct irq_host *h, struct device_node *node)
static int qe_ic_host_match(struct irq_domain *h, struct device_node *node)
{
/* Exact match, unless qe_ic node is NULL */
return h->of_node == NULL || h->of_node == node;
}
static int qe_ic_host_map(struct irq_host *h, unsigned int virq,
static int qe_ic_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
struct qe_ic *qe_ic = h->host_data;
......@@ -272,23 +272,10 @@ static int qe_ic_host_map(struct irq_host *h, unsigned int virq,
return 0;
}
static int qe_ic_host_xlate(struct irq_host *h, struct device_node *ct,
const u32 * intspec, unsigned int intsize,
irq_hw_number_t * out_hwirq,
unsigned int *out_flags)
{
*out_hwirq = intspec[0];
if (intsize > 1)
*out_flags = intspec[1];
else
*out_flags = IRQ_TYPE_NONE;
return 0;
}
static struct irq_host_ops qe_ic_host_ops = {
static struct irq_domain_ops qe_ic_host_ops = {
.match = qe_ic_host_match,
.map = qe_ic_host_map,
.xlate = qe_ic_host_xlate,
.xlate = irq_domain_xlate_onetwocell,
};
/* Return an interrupt vector or NO_IRQ if no interrupt is pending. */
......@@ -339,8 +326,8 @@ void __init qe_ic_init(struct device_node *node, unsigned int flags,
if (qe_ic == NULL)
return;
qe_ic->irqhost = irq_alloc_host(node, IRQ_HOST_MAP_LINEAR,
NR_QE_IC_INTS, &qe_ic_host_ops, 0);
qe_ic->irqhost = irq_domain_add_linear(node, NR_QE_IC_INTS,
&qe_ic_host_ops, qe_ic);
if (qe_ic->irqhost == NULL) {
kfree(qe_ic);
return;
......@@ -348,7 +335,6 @@ void __init qe_ic_init(struct device_node *node, unsigned int flags,
qe_ic->regs = ioremap(res.start, resource_size(&res));
qe_ic->irqhost->host_data = qe_ic;
qe_ic->hc_irq = qe_ic_irq_chip;
qe_ic->virq_high = irq_of_parse_and_map(node, 0);
......
......@@ -79,7 +79,7 @@ struct qe_ic {
volatile u32 __iomem *regs;
/* The remapper for this QEIC */
struct irq_host *irqhost;
struct irq_domain *irqhost;
/* The "linux" controller struct */
struct irq_chip hc_irq;
......
......@@ -51,7 +51,7 @@
u32 tsi108_pci_cfg_base;
static u32 tsi108_pci_cfg_phys;
u32 tsi108_csr_vir_base;
static struct irq_host *pci_irq_host;
static struct irq_domain *pci_irq_host;
extern u32 get_vir_csrbase(void);
extern u32 tsi108_read_reg(u32 reg_offset);
......@@ -376,7 +376,7 @@ static struct irq_chip tsi108_pci_irq = {
.irq_unmask = tsi108_pci_irq_unmask,
};
static int pci_irq_host_xlate(struct irq_host *h, struct device_node *ct,
static int pci_irq_host_xlate(struct irq_domain *h, struct device_node *ct,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_flags)
{
......@@ -385,7 +385,7 @@ static int pci_irq_host_xlate(struct irq_host *h, struct device_node *ct,
return 0;
}
static int pci_irq_host_map(struct irq_host *h, unsigned int virq,
static int pci_irq_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{ unsigned int irq;
DBG("%s(%d, 0x%lx)\n", __func__, virq, hw);
......@@ -397,7 +397,7 @@ static int pci_irq_host_map(struct irq_host *h, unsigned int virq,
return 0;
}
static struct irq_host_ops pci_irq_host_ops = {
static struct irq_domain_ops pci_irq_domain_ops = {
.map = pci_irq_host_map,
.xlate = pci_irq_host_xlate,
};
......@@ -419,10 +419,9 @@ void __init tsi108_pci_int_init(struct device_node *node)
{
DBG("Tsi108_pci_int_init: initializing PCI interrupts\n");
pci_irq_host = irq_alloc_host(node, IRQ_HOST_MAP_LEGACY,
0, &pci_irq_host_ops, 0);
pci_irq_host = irq_domain_add_legacy_isa(node, &pci_irq_domain_ops, NULL);
if (pci_irq_host == NULL) {
printk(KERN_ERR "pci_irq_host: failed to allocate irq host !\n");
printk(KERN_ERR "pci_irq_host: failed to allocate irq domain!\n");
return;
}
......
......@@ -49,7 +49,7 @@ struct uic {
raw_spinlock_t lock;
/* The remapper for this UIC */
struct irq_host *irqhost;
struct irq_domain *irqhost;
};
static void uic_unmask_irq(struct irq_data *d)
......@@ -174,7 +174,7 @@ static struct irq_chip uic_irq_chip = {
.irq_set_type = uic_set_irq_type,
};
static int uic_host_map(struct irq_host *h, unsigned int virq,
static int uic_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
struct uic *uic = h->host_data;
......@@ -190,21 +190,9 @@ static int uic_host_map(struct irq_host *h, unsigned int virq,
return 0;
}
static int uic_host_xlate(struct irq_host *h, struct device_node *ct,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_type)
{
/* UIC intspecs must have 2 cells */
BUG_ON(intsize != 2);
*out_hwirq = intspec[0];
*out_type = intspec[1];
return 0;
}
static struct irq_host_ops uic_host_ops = {
static struct irq_domain_ops uic_host_ops = {
.map = uic_host_map,
.xlate = uic_host_xlate,
.xlate = irq_domain_xlate_twocell,
};
void uic_irq_cascade(unsigned int virq, struct irq_desc *desc)
......@@ -270,13 +258,11 @@ static struct uic * __init uic_init_one(struct device_node *node)
}
uic->dcrbase = *dcrreg;
uic->irqhost = irq_alloc_host(node, IRQ_HOST_MAP_LINEAR,
NR_UIC_INTS, &uic_host_ops, -1);
uic->irqhost = irq_domain_add_linear(node, NR_UIC_INTS, &uic_host_ops,
uic);
if (! uic->irqhost)
return NULL; /* FIXME: panic? */
uic->irqhost->host_data = uic;
/* Start with all interrupts disabled, level and non-critical */
mtdcr(uic->dcrbase + UIC_ER, 0);
mtdcr(uic->dcrbase + UIC_CR, 0);
......
......@@ -40,7 +40,7 @@ unsigned int xics_interrupt_server_size = 8;
DEFINE_PER_CPU(struct xics_cppr, xics_cppr);
struct irq_host *xics_host;
struct irq_domain *xics_host;
static LIST_HEAD(ics_list);
......@@ -212,16 +212,16 @@ void xics_migrate_irqs_away(void)
/* We can't set affinity on ISA interrupts */
if (virq < NUM_ISA_INTERRUPTS)
continue;
if (!virq_is_host(virq, xics_host))
continue;
irq = (unsigned int)virq_to_hw(virq);
/* We need to get IPIs still. */
if (irq == XICS_IPI || irq == XICS_IRQ_SPURIOUS)
continue;
desc = irq_to_desc(virq);
/* We only need to migrate enabled IRQS */
if (!desc || !desc->action)
continue;
if (desc->irq_data.domain != xics_host)
continue;
irq = desc->irq_data.hwirq;
/* We need to get IPIs still. */
if (irq == XICS_IPI || irq == XICS_IRQ_SPURIOUS)
continue;
chip = irq_desc_get_chip(desc);
if (!chip || !chip->irq_set_affinity)
continue;
......@@ -301,7 +301,7 @@ int xics_get_irq_server(unsigned int virq, const struct cpumask *cpumask,
}
#endif /* CONFIG_SMP */
static int xics_host_match(struct irq_host *h, struct device_node *node)
static int xics_host_match(struct irq_domain *h, struct device_node *node)
{
struct ics *ics;
......@@ -323,7 +323,7 @@ static struct irq_chip xics_ipi_chip = {
.irq_unmask = xics_ipi_unmask,
};
static int xics_host_map(struct irq_host *h, unsigned int virq,
static int xics_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
struct ics *ics;
......@@ -351,7 +351,7 @@ static int xics_host_map(struct irq_host *h, unsigned int virq,
return -EINVAL;
}
static int xics_host_xlate(struct irq_host *h, struct device_node *ct,
static int xics_host_xlate(struct irq_domain *h, struct device_node *ct,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_flags)
......@@ -366,7 +366,7 @@ static int xics_host_xlate(struct irq_host *h, struct device_node *ct,
return 0;
}
static struct irq_host_ops xics_host_ops = {
static struct irq_domain_ops xics_host_ops = {
.match = xics_host_match,
.map = xics_host_map,
.xlate = xics_host_xlate,
......@@ -374,8 +374,7 @@ static struct irq_host_ops xics_host_ops = {
static void __init xics_init_host(void)
{
xics_host = irq_alloc_host(NULL, IRQ_HOST_MAP_TREE, 0, &xics_host_ops,
XICS_IRQ_SPURIOUS);
xics_host = irq_domain_add_tree(NULL, &xics_host_ops, NULL);
BUG_ON(xics_host == NULL);
irq_set_default_host(xics_host);
}
......
......@@ -40,7 +40,7 @@
#define XINTC_IVR 24 /* Interrupt Vector */
#define XINTC_MER 28 /* Master Enable */
static struct irq_host *master_irqhost;
static struct irq_domain *master_irqhost;
#define XILINX_INTC_MAXIRQS (32)
......@@ -141,7 +141,7 @@ static struct irq_chip xilinx_intc_edge_irqchip = {
/**
* xilinx_intc_xlate - translate virq# from device tree interrupts property
*/
static int xilinx_intc_xlate(struct irq_host *h, struct device_node *ct,
static int xilinx_intc_xlate(struct irq_domain *h, struct device_node *ct,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq,
unsigned int *out_flags)
......@@ -161,7 +161,7 @@ static int xilinx_intc_xlate(struct irq_host *h, struct device_node *ct,
return 0;
}
static int xilinx_intc_map(struct irq_host *h, unsigned int virq,
static int xilinx_intc_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t irq)
{
irq_set_chip_data(virq, h->host_data);
......@@ -177,15 +177,15 @@ static int xilinx_intc_map(struct irq_host *h, unsigned int virq,
return 0;
}
static struct irq_host_ops xilinx_intc_ops = {
static struct irq_domain_ops xilinx_intc_ops = {
.map = xilinx_intc_map,
.xlate = xilinx_intc_xlate,
};
struct irq_host * __init
struct irq_domain * __init
xilinx_intc_init(struct device_node *np)
{
struct irq_host * irq;
struct irq_domain * irq;
void * regs;
/* Find and map the intc registers */
......@@ -200,12 +200,11 @@ xilinx_intc_init(struct device_node *np)
out_be32(regs + XINTC_IAR, ~(u32) 0); /* Acknowledge pending irqs */
out_be32(regs + XINTC_MER, 0x3UL); /* Turn on the Master Enable. */
/* Allocate and initialize an irq_host structure. */
irq = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, XILINX_INTC_MAXIRQS,
&xilinx_intc_ops, -1);
/* Allocate and initialize an irq_domain structure. */
irq = irq_domain_add_linear(np, XILINX_INTC_MAXIRQS, &xilinx_intc_ops,
regs);
if (!irq)
panic(__FILE__ ": Cannot allocate IRQ host\n");
irq->host_data = regs;
return irq;
}
......
......@@ -22,6 +22,7 @@
#include <linux/proc_fs.h>
#include <linux/mutex.h>
#include <linux/atomic.h>
#include <linux/irqdomain.h>
#define OF_ROOT_NODE_ADDR_CELLS_DEFAULT 2
#define OF_ROOT_NODE_SIZE_CELLS_DEFAULT 1
......@@ -55,15 +56,6 @@ struct resource;
extern void __iomem *of_ioremap(struct resource *res, unsigned long offset, unsigned long size, char *name);
extern void of_iounmap(struct resource *res, void __iomem *base, unsigned long size);
/* These routines are here to provide compatibility with how powerpc
* handles IRQ mapping for OF device nodes. We precompute and permanently
* register them in the platform_device objects, whereas powerpc computes them
* on request.
*/
static inline void irq_dispose_mapping(unsigned int virq)
{
}
extern struct device_node *of_console_device;
extern char *of_console_path;
extern char *of_console_options;
......
......@@ -402,6 +402,7 @@ config X86_INTEL_CE
select X86_REBOOTFIXUPS
select OF
select OF_EARLY_FLATTREE
select IRQ_DOMAIN
---help---
Select for the Intel CE media processor (CE4100) SOC.
This option compiles in support for the CE4100 SOC for settop
......@@ -2080,6 +2081,7 @@ config OLPC
select GPIOLIB
select OF
select OF_PROMTREE
select IRQ_DOMAIN
---help---
Add support for detecting the unique features of the OLPC
XO hardware.
......
#ifndef __IRQ_CONTROLLER__
#define __IRQ_CONTROLLER__
struct irq_domain {
int (*xlate)(struct irq_domain *h, const u32 *intspec, u32 intsize,
u32 *out_hwirq, u32 *out_type);
void *priv;
struct device_node *controller;
struct list_head l;
};
#endif
......@@ -21,7 +21,6 @@
#include <asm/irq.h>
#include <linux/atomic.h>
#include <asm/setup.h>
#include <asm/irq_controller.h>
#ifdef CONFIG_OF
extern int of_ioapic;
......@@ -43,15 +42,6 @@ extern char cmd_line[COMMAND_LINE_SIZE];
#define pci_address_to_pio pci_address_to_pio
unsigned long pci_address_to_pio(phys_addr_t addr);
/**
* irq_dispose_mapping - Unmap an interrupt
* @virq: linux virq number of the interrupt to unmap
*
* FIXME: We really should implement proper virq handling like power,
* but that's going to be major surgery.
*/
static inline void irq_dispose_mapping(unsigned int virq) { }
#define HAVE_ARCH_DEVTREE_FIXUPS
#endif /* __ASSEMBLY__ */
......
......@@ -4,6 +4,7 @@
#include <linux/bootmem.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/irqdomain.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/of.h>
......@@ -17,64 +18,14 @@
#include <linux/initrd.h>
#include <asm/hpet.h>
#include <asm/irq_controller.h>
#include <asm/apic.h>
#include <asm/pci_x86.h>
__initdata u64 initial_dtb;
char __initdata cmd_line[COMMAND_LINE_SIZE];
static LIST_HEAD(irq_domains);
static DEFINE_RAW_SPINLOCK(big_irq_lock);
int __initdata of_ioapic;
#ifdef CONFIG_X86_IO_APIC
static void add_interrupt_host(struct irq_domain *ih)
{
unsigned long flags;
raw_spin_lock_irqsave(&big_irq_lock, flags);
list_add(&ih->l, &irq_domains);
raw_spin_unlock_irqrestore(&big_irq_lock, flags);
}
#endif
static struct irq_domain *get_ih_from_node(struct device_node *controller)
{
struct irq_domain *ih, *found = NULL;
unsigned long flags;
raw_spin_lock_irqsave(&big_irq_lock, flags);
list_for_each_entry(ih, &irq_domains, l) {
if (ih->controller == controller) {
found = ih;
break;
}
}
raw_spin_unlock_irqrestore(&big_irq_lock, flags);
return found;
}
unsigned int irq_create_of_mapping(struct device_node *controller,
const u32 *intspec, unsigned int intsize)
{
struct irq_domain *ih;
u32 virq, type;
int ret;
ih = get_ih_from_node(controller);
if (!ih)
return 0;
ret = ih->xlate(ih, intspec, intsize, &virq, &type);
if (ret)
return 0;
if (type == IRQ_TYPE_NONE)
return virq;
irq_set_irq_type(virq, type);
return virq;
}
EXPORT_SYMBOL_GPL(irq_create_of_mapping);
unsigned long pci_address_to_pio(phys_addr_t address)
{
/*
......@@ -354,36 +305,43 @@ static struct of_ioapic_type of_ioapic_type[] =
},
};
static int ioapic_xlate(struct irq_domain *id, const u32 *intspec, u32 intsize,
u32 *out_hwirq, u32 *out_type)
static int ioapic_xlate(struct irq_domain *domain,
struct device_node *controller,
const u32 *intspec, u32 intsize,
irq_hw_number_t *out_hwirq, u32 *out_type)
{
struct mp_ioapic_gsi *gsi_cfg;
struct io_apic_irq_attr attr;
struct of_ioapic_type *it;
u32 line, idx, type;
u32 line, idx;
int rc;
if (intsize < 2)
if (WARN_ON(intsize < 2))
return -EINVAL;
line = *intspec;
idx = (u32) id->priv;
gsi_cfg = mp_ioapic_gsi_routing(idx);
*out_hwirq = line + gsi_cfg->gsi_base;
intspec++;
type = *intspec;
line = intspec[0];
if (type >= ARRAY_SIZE(of_ioapic_type))
if (intspec[1] >= ARRAY_SIZE(of_ioapic_type))
return -EINVAL;
it = of_ioapic_type + type;
*out_type = it->out_type;
it = &of_ioapic_type[intspec[1]];
idx = (u32) domain->host_data;
set_io_apic_irq_attr(&attr, idx, line, it->trigger, it->polarity);
return io_apic_setup_irq_pin_once(*out_hwirq, cpu_to_node(0), &attr);
rc = io_apic_setup_irq_pin_once(irq_find_mapping(domain, line),
cpu_to_node(0), &attr);
if (rc)
return rc;
*out_hwirq = line;
*out_type = it->out_type;
return 0;
}
const struct irq_domain_ops ioapic_irq_domain_ops = {
.xlate = ioapic_xlate,
};
static void __init ioapic_add_ofnode(struct device_node *np)
{
struct resource r;
......@@ -399,13 +357,14 @@ static void __init ioapic_add_ofnode(struct device_node *np)
for (i = 0; i < nr_ioapics; i++) {
if (r.start == mpc_ioapic_addr(i)) {
struct irq_domain *id;
struct mp_ioapic_gsi *gsi_cfg;
gsi_cfg = mp_ioapic_gsi_routing(i);
id = kzalloc(sizeof(*id), GFP_KERNEL);
id = irq_domain_add_legacy(np, 32, gsi_cfg->gsi_base, 0,
&ioapic_irq_domain_ops,
(void*)i);
BUG_ON(!id);
id->controller = np;
id->xlate = ioapic_xlate;
id->priv = (void *)i;
add_interrupt_host(id);
return;
}
}
......
......@@ -37,7 +37,7 @@ struct mpc8xxx_gpio_chip {
* open drain mode safely
*/
u32 data;
struct irq_host *irq;
struct irq_domain *irq;
void *of_dev_id_data;
};
......@@ -281,7 +281,7 @@ static struct irq_chip mpc8xxx_irq_chip = {
.irq_set_type = mpc8xxx_irq_set_type,
};
static int mpc8xxx_gpio_irq_map(struct irq_host *h, unsigned int virq,
static int mpc8xxx_gpio_irq_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
struct mpc8xxx_gpio_chip *mpc8xxx_gc = h->host_data;
......@@ -296,24 +296,9 @@ static int mpc8xxx_gpio_irq_map(struct irq_host *h, unsigned int virq,
return 0;
}
static int mpc8xxx_gpio_irq_xlate(struct irq_host *h, struct device_node *ct,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq,
unsigned int *out_flags)
{
/* interrupt sense values coming from the device tree equal either
* EDGE_FALLING or EDGE_BOTH
*/
*out_hwirq = intspec[0];
*out_flags = intspec[1];
return 0;
}
static struct irq_host_ops mpc8xxx_gpio_irq_ops = {
static struct irq_domain_ops mpc8xxx_gpio_irq_ops = {
.map = mpc8xxx_gpio_irq_map,
.xlate = mpc8xxx_gpio_irq_xlate,
.xlate = irq_domain_xlate_twocell,
};
static struct of_device_id mpc8xxx_gpio_ids[] __initdata = {
......@@ -364,9 +349,8 @@ static void __init mpc8xxx_add_controller(struct device_node *np)
if (hwirq == NO_IRQ)
goto skip_irq;
mpc8xxx_gc->irq =
irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, MPC8XXX_GPIO_PINS,
&mpc8xxx_gpio_irq_ops, MPC8XXX_GPIO_PINS);
mpc8xxx_gc->irq = irq_domain_add_linear(np, MPC8XXX_GPIO_PINS,
&mpc8xxx_gpio_irq_ops, mpc8xxx_gc);
if (!mpc8xxx_gc->irq)
goto skip_irq;
......@@ -374,8 +358,6 @@ static void __init mpc8xxx_add_controller(struct device_node *np)
if (id)
mpc8xxx_gc->of_dev_id_data = id->data;
mpc8xxx_gc->irq->host_data = mpc8xxx_gc;
/* ack and mask all irqs */
out_be32(mm_gc->regs + GPIO_IER, 0xffffffff);
out_be32(mm_gc->regs + GPIO_IMR, 0);
......
......@@ -201,6 +201,7 @@ config MENELAUS
config TWL4030_CORE
bool "Texas Instruments TWL4030/TWL5030/TWL6030/TPS659x0 Support"
depends on I2C=y && GENERIC_HARDIRQS
select IRQ_DOMAIN
help
Say yes here if you have TWL4030 / TWL6030 family chip on your board.
This core driver provides register access and IRQ handling
......
......@@ -38,6 +38,7 @@
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/regulator/machine.h>
......@@ -149,7 +150,7 @@
#define TWL_MODULE_LAST TWL4030_MODULE_LAST
#define TWL4030_NR_IRQS 8
#define TWL4030_NR_IRQS 34 /* core:8, power:8, gpio: 18 */
#define TWL6030_NR_IRQS 20
/* Base Address defns for twl4030_map[] */
......@@ -263,10 +264,6 @@ struct twl_client {
static struct twl_client twl_modules[TWL_NUM_SLAVES];
#ifdef CONFIG_IRQ_DOMAIN
static struct irq_domain domain;
#endif
/* mapping the module id to slave id and base address */
struct twl_mapping {
unsigned char sid; /* Slave ID */
......@@ -1227,14 +1224,8 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
pdata->irq_base = status;
pdata->irq_end = pdata->irq_base + nr_irqs;
#ifdef CONFIG_IRQ_DOMAIN
domain.irq_base = pdata->irq_base;
domain.nr_irq = nr_irqs;
domain.of_node = of_node_get(node);
domain.ops = &irq_domain_simple_ops;
irq_domain_add(&domain);
#endif
irq_domain_add_legacy(node, nr_irqs, pdata->irq_base, 0,
&irq_domain_simple_ops, NULL);
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) {
dev_dbg(&client->dev, "can't talk I2C?\n");
......@@ -1315,11 +1306,10 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
twl_i2c_write_u8(TWL4030_MODULE_INTBR, temp, REG_GPPUPDCTR1);
}
#ifdef CONFIG_OF_DEVICE
status = -ENODEV;
if (node)
status = of_platform_populate(node, NULL, NULL, &client->dev);
else
#endif
if (status)
status = add_children(pdata, id->driver_data);
fail:
......
......@@ -255,13 +255,13 @@ static inline int __init mdio_ofgpio_init(void)
return platform_driver_register(&mdio_ofgpio_driver);
}
static inline void __exit mdio_ofgpio_exit(void)
static inline void mdio_ofgpio_exit(void)
{
platform_driver_unregister(&mdio_ofgpio_driver);
}
#else
static inline int __init mdio_ofgpio_init(void) { return 0; }
static inline void __exit mdio_ofgpio_exit(void) { }
static inline void mdio_ofgpio_exit(void) { }
#endif /* CONFIG_OF_GPIO */
static struct platform_driver mdio_gpio_driver = {
......
......@@ -55,7 +55,7 @@ EXPORT_SYMBOL(of_find_device_by_node);
#include <asm/dcr.h>
#endif
#if !defined(CONFIG_SPARC)
#ifdef CONFIG_OF_ADDRESS
/*
* The following routines scan a subtree and registers a device for
* each applicable node.
......@@ -462,4 +462,4 @@ int of_platform_populate(struct device_node *root,
of_node_put(root);
return rc;
}
#endif /* !CONFIG_SPARC */
#endif /* CONFIG_OF_ADDRESS */
......@@ -9,99 +9,182 @@
* representation into a hardware irq number that can be mapped back to a
* Linux irq number without any extra platform support code.
*
* irq_domain is expected to be embedded in an interrupt controller's private
* data structure.
* Interrupt controller "domain" data structure. This could be defined as a
* irq domain controller. That is, it handles the mapping between hardware
* and virtual interrupt numbers for a given interrupt domain. The domain
* structure is generally created by the PIC code for a given PIC instance
* (though a domain can cover more than one PIC if they have a flat number
* model). It's the domain callbacks that are responsible for setting the
* irq_chip on a given irq_desc after it's been mapped.
*
* The host code and data structures are agnostic to whether or not
* we use an open firmware device-tree. We do have references to struct
* device_node in two places: in irq_find_host() to find the host matching
* a given interrupt controller node, and of course as an argument to its
* counterpart domain->ops->match() callback. However, those are treated as
* generic pointers by the core and the fact that it's actually a device-node
* pointer is purely a convention between callers and implementation. This
* code could thus be used on other architectures by replacing those two
* by some sort of arch-specific void * "token" used to identify interrupt
* controllers.
*/
#ifndef _LINUX_IRQDOMAIN_H
#define _LINUX_IRQDOMAIN_H
#include <linux/irq.h>
#include <linux/mod_devicetable.h>
#include <linux/types.h>
#include <linux/radix-tree.h>
#ifdef CONFIG_IRQ_DOMAIN
struct device_node;
struct irq_domain;
struct of_device_id;
/* Number of irqs reserved for a legacy isa controller */
#define NUM_ISA_INTERRUPTS 16
/* This type is the placeholder for a hardware interrupt number. It has to
* be big enough to enclose whatever representation is used by a given
* platform.
*/
typedef unsigned long irq_hw_number_t;
/**
* struct irq_domain_ops - Methods for irq_domain objects
* @to_irq: (optional) given a local hardware irq number, return the linux
* irq number. If to_irq is not implemented, then the irq_domain
* will use this translation: irq = (domain->irq_base + hwirq)
* @dt_translate: Given a device tree node and interrupt specifier, decode
* @match: Match an interrupt controller device node to a host, returns
* 1 on a match
* @map: Create or update a mapping between a virtual irq number and a hw
* irq number. This is called only once for a given mapping.
* @unmap: Dispose of such a mapping
* @xlate: Given a device tree node and interrupt specifier, decode
* the hardware irq number and linux irq type value.
*
* Functions below are provided by the driver and called whenever a new mapping
* is created or an old mapping is disposed. The driver can then proceed to
* whatever internal data structures management is required. It also needs
* to setup the irq_desc when returning from map().
*/
struct irq_domain_ops {
unsigned int (*to_irq)(struct irq_domain *d, unsigned long hwirq);
#ifdef CONFIG_OF
int (*dt_translate)(struct irq_domain *d, struct device_node *node,
int (*match)(struct irq_domain *d, struct device_node *node);
int (*map)(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw);
void (*unmap)(struct irq_domain *d, unsigned int virq);
int (*xlate)(struct irq_domain *d, struct device_node *node,
const u32 *intspec, unsigned int intsize,
unsigned long *out_hwirq, unsigned int *out_type);
#endif /* CONFIG_OF */
};
/**
* struct irq_domain - Hardware interrupt number translation object
* @list: Element in global irq_domain list.
* @link: Element in global irq_domain list.
* @revmap_type: Method used for reverse mapping hwirq numbers to linux irq. This
* will be one of the IRQ_DOMAIN_MAP_* values.
* @revmap_data: Revmap method specific data.
* @ops: pointer to irq_domain methods
* @host_data: private data pointer for use by owner. Not touched by irq_domain
* core code.
* @irq_base: Start of irq_desc range assigned to the irq_domain. The creator
* of the irq_domain is responsible for allocating the array of
* irq_desc structures.
* @nr_irq: Number of irqs managed by the irq domain
* @hwirq_base: Starting number for hwirqs managed by the irq domain
* @ops: pointer to irq_domain methods
* @priv: private data pointer for use by owner. Not touched by irq_domain
* core code.
* @of_node: (optional) Pointer to device tree nodes associated with the
* irq_domain. Used when decoding device tree interrupt specifiers.
*/
struct irq_domain {
struct list_head list;
unsigned int irq_base;
unsigned int nr_irq;
unsigned int hwirq_base;
struct list_head link;
/* type of reverse mapping_technique */
unsigned int revmap_type;
union {
struct {
unsigned int size;
unsigned int first_irq;
irq_hw_number_t first_hwirq;
} legacy;
struct {
unsigned int size;
unsigned int *revmap;
} linear;
struct radix_tree_root tree;
} revmap_data;
const struct irq_domain_ops *ops;
void *priv;
void *host_data;
irq_hw_number_t inval_irq;
/* Optional device node pointer */
struct device_node *of_node;
};
/**
* irq_domain_to_irq() - Translate from a hardware irq to a linux irq number
*
* Returns the linux irq number associated with a hardware irq. By default,
* the mapping is irq == domain->irq_base + hwirq, but this mapping can
* be overridden if the irq_domain implements a .to_irq() hook.
*/
static inline unsigned int irq_domain_to_irq(struct irq_domain *d,
unsigned long hwirq)
#ifdef CONFIG_IRQ_DOMAIN
struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
unsigned int size,
unsigned int first_irq,
irq_hw_number_t first_hwirq,
const struct irq_domain_ops *ops,
void *host_data);
struct irq_domain *irq_domain_add_linear(struct device_node *of_node,
unsigned int size,
const struct irq_domain_ops *ops,
void *host_data);
struct irq_domain *irq_domain_add_nomap(struct device_node *of_node,
const struct irq_domain_ops *ops,
void *host_data);
struct irq_domain *irq_domain_add_tree(struct device_node *of_node,
const struct irq_domain_ops *ops,
void *host_data);
extern struct irq_domain *irq_find_host(struct device_node *node);
extern void irq_set_default_host(struct irq_domain *host);
extern void irq_set_virq_count(unsigned int count);
static inline struct irq_domain *irq_domain_add_legacy_isa(
struct device_node *of_node,
const struct irq_domain_ops *ops,
void *host_data)
{
if (d->ops->to_irq)
return d->ops->to_irq(d, hwirq);
if (WARN_ON(hwirq < d->hwirq_base))
return 0;
return d->irq_base + hwirq - d->hwirq_base;
return irq_domain_add_legacy(of_node, NUM_ISA_INTERRUPTS, 0, 0, ops,
host_data);
}
extern struct irq_domain *irq_find_host(struct device_node *node);
extern void irq_set_default_host(struct irq_domain *host);
extern void irq_set_virq_count(unsigned int count);
#define irq_domain_for_each_hwirq(d, hw) \
for (hw = d->hwirq_base; hw < d->hwirq_base + d->nr_irq; hw++)
#define irq_domain_for_each_irq(d, hw, irq) \
for (hw = d->hwirq_base, irq = irq_domain_to_irq(d, hw); \
hw < d->hwirq_base + d->nr_irq; \
hw++, irq = irq_domain_to_irq(d, hw))
extern unsigned int irq_create_mapping(struct irq_domain *host,
irq_hw_number_t hwirq);
extern void irq_dispose_mapping(unsigned int virq);
extern unsigned int irq_find_mapping(struct irq_domain *host,
irq_hw_number_t hwirq);
extern unsigned int irq_create_direct_mapping(struct irq_domain *host);
extern void irq_radix_revmap_insert(struct irq_domain *host, unsigned int virq,
irq_hw_number_t hwirq);
extern unsigned int irq_radix_revmap_lookup(struct irq_domain *host,
irq_hw_number_t hwirq);
extern unsigned int irq_linear_revmap(struct irq_domain *host,
irq_hw_number_t hwirq);
extern void irq_domain_add(struct irq_domain *domain);
extern void irq_domain_del(struct irq_domain *domain);
extern const struct irq_domain_ops irq_domain_simple_ops;
extern struct irq_domain_ops irq_domain_simple_ops;
#endif /* CONFIG_IRQ_DOMAIN */
/* stock xlate functions */
int irq_domain_xlate_onecell(struct irq_domain *d, struct device_node *ctrlr,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_type);
int irq_domain_xlate_twocell(struct irq_domain *d, struct device_node *ctrlr,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_type);
int irq_domain_xlate_onetwocell(struct irq_domain *d, struct device_node *ctrlr,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_type);
#if defined(CONFIG_IRQ_DOMAIN) && defined(CONFIG_OF_IRQ)
extern void irq_domain_add_simple(struct device_node *controller, int irq_base);
#if defined(CONFIG_OF_IRQ)
extern void irq_domain_generate_simple(const struct of_device_id *match,
u64 phys_base, unsigned int irq_start);
#else /* CONFIG_IRQ_DOMAIN && CONFIG_OF_IRQ */
#else /* CONFIG_OF_IRQ */
static inline void irq_domain_generate_simple(const struct of_device_id *match,
u64 phys_base, unsigned int irq_start) { }
#endif /* CONFIG_IRQ_DOMAIN && CONFIG_OF_IRQ */
#endif /* !CONFIG_OF_IRQ */
#else /* CONFIG_IRQ_DOMAIN */
static inline void irq_dispose_mapping(unsigned int virq) { }
#endif /* !CONFIG_IRQ_DOMAIN */
#endif /* _LINUX_IRQDOMAIN_H */
......@@ -4,6 +4,7 @@
#include <linux/errno.h>
#include <linux/of.h>
#ifdef CONFIG_OF_ADDRESS
extern u64 of_translate_address(struct device_node *np, const __be32 *addr);
extern int of_address_to_resource(struct device_node *dev, int index,
struct resource *r);
......@@ -25,12 +26,37 @@ static inline unsigned long pci_address_to_pio(phys_addr_t addr) { return -1; }
#define pci_address_to_pio pci_address_to_pio
#endif
#ifdef CONFIG_PCI
#else /* CONFIG_OF_ADDRESS */
static inline int of_address_to_resource(struct device_node *dev, int index,
struct resource *r)
{
return -EINVAL;
}
static inline struct device_node *of_find_matching_node_by_address(
struct device_node *from,
const struct of_device_id *matches,
u64 base_address)
{
return NULL;
}
static inline void __iomem *of_iomap(struct device_node *device, int index)
{
return NULL;
}
static inline const u32 *of_get_address(struct device_node *dev, int index,
u64 *size, unsigned int *flags)
{
return NULL;
}
#endif /* CONFIG_OF_ADDRESS */
#if defined(CONFIG_OF_ADDRESS) && defined(CONFIG_PCI)
extern const __be32 *of_get_pci_address(struct device_node *dev, int bar_no,
u64 *size, unsigned int *flags);
extern int of_pci_address_to_resource(struct device_node *dev, int bar,
struct resource *r);
#else /* CONFIG_PCI */
#else /* CONFIG_OF_ADDRESS && CONFIG_PCI */
static inline int of_pci_address_to_resource(struct device_node *dev, int bar,
struct resource *r)
{
......@@ -42,8 +68,7 @@ static inline const __be32 *of_get_pci_address(struct device_node *dev,
{
return NULL;
}
#endif /* CONFIG_PCI */
#endif /* CONFIG_OF_ADDRESS && CONFIG_PCI */
#endif /* __OF_ADDRESS_H */
......@@ -6,6 +6,7 @@ struct of_irq;
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/ioport.h>
#include <linux/of.h>
......@@ -65,9 +66,6 @@ extern int of_irq_map_one(struct device_node *device, int index,
extern unsigned int irq_create_of_mapping(struct device_node *controller,
const u32 *intspec,
unsigned int intsize);
#ifdef CONFIG_IRQ_DOMAIN
extern void irq_dispose_mapping(unsigned int irq);
#endif
extern int of_irq_to_resource(struct device_node *dev, int index,
struct resource *r);
extern int of_irq_count(struct device_node *dev);
......
......@@ -81,7 +81,7 @@ extern struct platform_device *of_device_alloc(struct device_node *np,
struct device *parent);
extern struct platform_device *of_find_device_by_node(struct device_node *np);
#if !defined(CONFIG_SPARC) /* SPARC has its own device registration method */
#ifdef CONFIG_OF_ADDRESS /* device reg helpers depend on OF_ADDRESS */
/* Platform devices and busses creation */
extern struct platform_device *of_platform_device_create(struct device_node *np,
const char *bus_id,
......@@ -94,8 +94,19 @@ extern int of_platform_populate(struct device_node *root,
const struct of_device_id *matches,
const struct of_dev_auxdata *lookup,
struct device *parent);
#endif /* !CONFIG_SPARC */
#endif /* CONFIG_OF_ADDRESS */
#endif /* CONFIG_OF_DEVICE */
#if !defined(CONFIG_OF_ADDRESS)
struct of_dev_auxdata;
static inline int of_platform_populate(struct device_node *root,
const struct of_device_id *matches,
const struct of_dev_auxdata *lookup,
struct device *parent)
{
return -ENODEV;
}
#endif /* !CONFIG_OF_ADDRESS */
#endif /* _LINUX_OF_PLATFORM_H */
#include <linux/debugfs.h>
#include <linux/hardirq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdesc.h>
#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/smp.h>
#include <linux/fs.h>
#define IRQ_DOMAIN_MAP_LEGACY 0 /* driver allocated fixed range of irqs.
* ie. legacy 8259, gets irqs 1..15 */
#define IRQ_DOMAIN_MAP_NOMAP 1 /* no fast reverse mapping */
#define IRQ_DOMAIN_MAP_LINEAR 2 /* linear map of interrupts */
#define IRQ_DOMAIN_MAP_TREE 3 /* radix tree */
static LIST_HEAD(irq_domain_list);
static DEFINE_MUTEX(irq_domain_mutex);
static DEFINE_MUTEX(revmap_trees_mutex);
static unsigned int irq_virq_count = NR_IRQS;
static struct irq_domain *irq_default_domain;
/**
* irq_domain_add() - Register an irq_domain
* @domain: ptr to initialized irq_domain structure
* irq_domain_alloc() - Allocate a new irq_domain data structure
* @of_node: optional device-tree node of the interrupt controller
* @revmap_type: type of reverse mapping to use
* @ops: map/unmap domain callbacks
* @host_data: Controller private data pointer
*
* Registers an irq_domain structure. The irq_domain must at a minimum be
* initialized with an ops structure pointer, and either a ->to_irq hook or
* a valid irq_base value. Everything else is optional.
* Allocates and initialize and irq_domain structure. Caller is expected to
* register allocated irq_domain with irq_domain_register(). Returns pointer
* to IRQ domain, or NULL on failure.
*/
void irq_domain_add(struct irq_domain *domain)
static struct irq_domain *irq_domain_alloc(struct device_node *of_node,
unsigned int revmap_type,
const struct irq_domain_ops *ops,
void *host_data)
{
struct irq_data *d;
int hwirq, irq;
struct irq_domain *domain;
/*
* This assumes that the irq_domain owner has already allocated
* the irq_descs. This block will be removed when support for dynamic
* allocation of irq_descs is added to irq_domain.
*/
irq_domain_for_each_irq(domain, hwirq, irq) {
d = irq_get_irq_data(irq);
if (!d) {
WARN(1, "error: assigning domain to non existant irq_desc");
return;
}
if (d->domain) {
/* things are broken; just report, don't clean up */
WARN(1, "error: irq_desc already assigned to a domain");
return;
domain = kzalloc(sizeof(*domain), GFP_KERNEL);
if (WARN_ON(!domain))
return NULL;
/* Fill structure */
domain->revmap_type = revmap_type;
domain->ops = ops;
domain->host_data = host_data;
domain->of_node = of_node_get(of_node);
return domain;
}
static void irq_domain_add(struct irq_domain *domain)
{
mutex_lock(&irq_domain_mutex);
list_add(&domain->link, &irq_domain_list);
mutex_unlock(&irq_domain_mutex);
pr_debug("irq: Allocated domain of type %d @0x%p\n",
domain->revmap_type, domain);
}
static unsigned int irq_domain_legacy_revmap(struct irq_domain *domain,
irq_hw_number_t hwirq)
{
irq_hw_number_t first_hwirq = domain->revmap_data.legacy.first_hwirq;
int size = domain->revmap_data.legacy.size;
if (WARN_ON(hwirq < first_hwirq || hwirq >= first_hwirq + size))
return 0;
return hwirq - first_hwirq + domain->revmap_data.legacy.first_irq;
}
/**
* irq_domain_add_legacy() - Allocate and register a legacy revmap irq_domain.
* @of_node: pointer to interrupt controller's device tree node.
* @size: total number of irqs in legacy mapping
* @first_irq: first number of irq block assigned to the domain
* @first_hwirq: first hwirq number to use for the translation. Should normally
* be '0', but a positive integer can be used if the effective
* hwirqs numbering does not begin at zero.
* @ops: map/unmap domain callbacks
* @host_data: Controller private data pointer
*
* Note: the map() callback will be called before this function returns
* for all legacy interrupts except 0 (which is always the invalid irq for
* a legacy controller).
*/
struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
unsigned int size,
unsigned int first_irq,
irq_hw_number_t first_hwirq,
const struct irq_domain_ops *ops,
void *host_data)
{
struct irq_domain *domain;
unsigned int i;
domain = irq_domain_alloc(of_node, IRQ_DOMAIN_MAP_LEGACY, ops, host_data);
if (!domain)
return NULL;
domain->revmap_data.legacy.first_irq = first_irq;
domain->revmap_data.legacy.first_hwirq = first_hwirq;
domain->revmap_data.legacy.size = size;
mutex_lock(&irq_domain_mutex);
/* Verify that all the irqs are available */
for (i = 0; i < size; i++) {
int irq = first_irq + i;
struct irq_data *irq_data = irq_get_irq_data(irq);
if (WARN_ON(!irq_data || irq_data->domain)) {
mutex_unlock(&irq_domain_mutex);
of_node_put(domain->of_node);
kfree(domain);
return NULL;
}
d->domain = domain;
d->hwirq = hwirq;
}
mutex_lock(&irq_domain_mutex);
list_add(&domain->list, &irq_domain_list);
/* Claim all of the irqs before registering a legacy domain */
for (i = 0; i < size; i++) {
struct irq_data *irq_data = irq_get_irq_data(first_irq + i);
irq_data->hwirq = first_hwirq + i;
irq_data->domain = domain;
}
mutex_unlock(&irq_domain_mutex);
for (i = 0; i < size; i++) {
int irq = first_irq + i;
int hwirq = first_hwirq + i;
/* IRQ0 gets ignored */
if (!irq)
continue;
/* Legacy flags are left to default at this point,
* one can then use irq_create_mapping() to
* explicitly change them
*/
ops->map(domain, irq, hwirq);
/* Clear norequest flags */
irq_clear_status_flags(irq, IRQ_NOREQUEST);
}
irq_domain_add(domain);
return domain;
}
/**
* irq_domain_add_linear() - Allocate and register a legacy revmap irq_domain.
* @of_node: pointer to interrupt controller's device tree node.
* @ops: map/unmap domain callbacks
* @host_data: Controller private data pointer
*/
struct irq_domain *irq_domain_add_linear(struct device_node *of_node,
unsigned int size,
const struct irq_domain_ops *ops,
void *host_data)
{
struct irq_domain *domain;
unsigned int *revmap;
revmap = kzalloc(sizeof(*revmap) * size, GFP_KERNEL);
if (WARN_ON(!revmap))
return NULL;
domain = irq_domain_alloc(of_node, IRQ_DOMAIN_MAP_LINEAR, ops, host_data);
if (!domain) {
kfree(revmap);
return NULL;
}
domain->revmap_data.linear.size = size;
domain->revmap_data.linear.revmap = revmap;
irq_domain_add(domain);
return domain;
}
struct irq_domain *irq_domain_add_nomap(struct device_node *of_node,
const struct irq_domain_ops *ops,
void *host_data)
{
struct irq_domain *domain = irq_domain_alloc(of_node,
IRQ_DOMAIN_MAP_NOMAP, ops, host_data);
if (domain)
irq_domain_add(domain);
return domain;
}
/**
* irq_domain_del() - Unregister an irq_domain
* @domain: ptr to registered irq_domain.
* irq_domain_add_tree()
* @of_node: pointer to interrupt controller's device tree node.
* @ops: map/unmap domain callbacks
*
* Note: The radix tree will be allocated later during boot automatically
* (the reverse mapping will use the slow path until that happens).
*/
void irq_domain_del(struct irq_domain *domain)
struct irq_domain *irq_domain_add_tree(struct device_node *of_node,
const struct irq_domain_ops *ops,
void *host_data)
{
struct irq_data *d;
int hwirq, irq;
struct irq_domain *domain = irq_domain_alloc(of_node,
IRQ_DOMAIN_MAP_TREE, ops, host_data);
if (domain) {
INIT_RADIX_TREE(&domain->revmap_data.tree, GFP_KERNEL);
irq_domain_add(domain);
}
return domain;
}
/**
* irq_find_host() - Locates a domain for a given device node
* @node: device-tree node of the interrupt controller
*/
struct irq_domain *irq_find_host(struct device_node *node)
{
struct irq_domain *h, *found = NULL;
int rc;
/* We might want to match the legacy controller last since
* it might potentially be set to match all interrupts in
* the absence of a device node. This isn't a problem so far
* yet though...
*/
mutex_lock(&irq_domain_mutex);
list_del(&domain->list);
list_for_each_entry(h, &irq_domain_list, link) {
if (h->ops->match)
rc = h->ops->match(h, node);
else
rc = (h->of_node != NULL) && (h->of_node == node);
if (rc) {
found = h;
break;
}
}
mutex_unlock(&irq_domain_mutex);
return found;
}
EXPORT_SYMBOL_GPL(irq_find_host);
/**
* irq_set_default_host() - Set a "default" irq domain
* @domain: default domain pointer
*
* For convenience, it's possible to set a "default" domain that will be used
* whenever NULL is passed to irq_create_mapping(). It makes life easier for
* platforms that want to manipulate a few hard coded interrupt numbers that
* aren't properly represented in the device-tree.
*/
void irq_set_default_host(struct irq_domain *domain)
{
pr_debug("irq: Default domain set to @0x%p\n", domain);
irq_default_domain = domain;
}
/**
* irq_set_virq_count() - Set the maximum number of linux irqs
* @count: number of linux irqs, capped with NR_IRQS
*
* This is mainly for use by platforms like iSeries who want to program
* the virtual irq number in the controller to avoid the reverse mapping
*/
void irq_set_virq_count(unsigned int count)
{
pr_debug("irq: Trying to set virq count to %d\n", count);
/* Clear the irq_domain assignments */
irq_domain_for_each_irq(domain, hwirq, irq) {
d = irq_get_irq_data(irq);
d->domain = NULL;
BUG_ON(count < NUM_ISA_INTERRUPTS);
if (count < NR_IRQS)
irq_virq_count = count;
}
static int irq_setup_virq(struct irq_domain *domain, unsigned int virq,
irq_hw_number_t hwirq)
{
struct irq_data *irq_data = irq_get_irq_data(virq);
irq_data->hwirq = hwirq;
irq_data->domain = domain;
if (domain->ops->map(domain, virq, hwirq)) {
pr_debug("irq: -> mapping failed, freeing\n");
irq_data->domain = NULL;
irq_data->hwirq = 0;
return -1;
}
irq_clear_status_flags(virq, IRQ_NOREQUEST);
return 0;
}
#if defined(CONFIG_OF_IRQ)
/**
* irq_create_of_mapping() - Map a linux irq number from a DT interrupt spec
* irq_create_direct_mapping() - Allocate an irq for direct mapping
* @domain: domain to allocate the irq for or NULL for default domain
*
* Used by the device tree interrupt mapping code to translate a device tree
* interrupt specifier to a valid linux irq number. Returns either a valid
* linux IRQ number or 0.
* This routine is used for irq controllers which can choose the hardware
* interrupt numbers they generate. In such a case it's simplest to use
* the linux irq as the hardware interrupt number.
*/
unsigned int irq_create_direct_mapping(struct irq_domain *domain)
{
unsigned int virq;
if (domain == NULL)
domain = irq_default_domain;
BUG_ON(domain == NULL);
WARN_ON(domain->revmap_type != IRQ_DOMAIN_MAP_NOMAP);
virq = irq_alloc_desc_from(1, 0);
if (!virq) {
pr_debug("irq: create_direct virq allocation failed\n");
return 0;
}
if (virq >= irq_virq_count) {
pr_err("ERROR: no free irqs available below %i maximum\n",
irq_virq_count);
irq_free_desc(virq);
return 0;
}
pr_debug("irq: create_direct obtained virq %d\n", virq);
if (irq_setup_virq(domain, virq, virq)) {
irq_free_desc(virq);
return 0;
}
return virq;
}
/**
* irq_create_mapping() - Map a hardware interrupt into linux irq space
* @domain: domain owning this hardware interrupt or NULL for default domain
* @hwirq: hardware irq number in that domain space
*
* When the caller no longer need the irq number returned by this function it
* should arrange to call irq_dispose_mapping().
* Only one mapping per hardware interrupt is permitted. Returns a linux
* irq number.
* If the sense/trigger is to be specified, set_irq_type() should be called
* on the number returned from that call.
*/
unsigned int irq_create_mapping(struct irq_domain *domain,
irq_hw_number_t hwirq)
{
unsigned int virq, hint;
pr_debug("irq: irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq);
/* Look for default domain if nececssary */
if (domain == NULL)
domain = irq_default_domain;
if (domain == NULL) {
printk(KERN_WARNING "irq_create_mapping called for"
" NULL domain, hwirq=%lx\n", hwirq);
WARN_ON(1);
return 0;
}
pr_debug("irq: -> using domain @%p\n", domain);
/* Check if mapping already exists */
virq = irq_find_mapping(domain, hwirq);
if (virq) {
pr_debug("irq: -> existing mapping on virq %d\n", virq);
return virq;
}
/* Get a virtual interrupt number */
if (domain->revmap_type == IRQ_DOMAIN_MAP_LEGACY)
return irq_domain_legacy_revmap(domain, hwirq);
/* Allocate a virtual interrupt number */
hint = hwirq % irq_virq_count;
if (hint == 0)
hint++;
virq = irq_alloc_desc_from(hint, 0);
if (!virq)
virq = irq_alloc_desc_from(1, 0);
if (!virq) {
pr_debug("irq: -> virq allocation failed\n");
return 0;
}
if (irq_setup_virq(domain, virq, hwirq)) {
if (domain->revmap_type != IRQ_DOMAIN_MAP_LEGACY)
irq_free_desc(virq);
return 0;
}
pr_debug("irq: irq %lu on domain %s mapped to virtual irq %u\n",
hwirq, domain->of_node ? domain->of_node->full_name : "null", virq);
return virq;
}
EXPORT_SYMBOL_GPL(irq_create_mapping);
unsigned int irq_create_of_mapping(struct device_node *controller,
const u32 *intspec, unsigned int intsize)
{
struct irq_domain *domain;
unsigned long hwirq;
unsigned int irq, type;
int rc = -EINVAL;
irq_hw_number_t hwirq;
unsigned int type = IRQ_TYPE_NONE;
unsigned int virq;
/* Find a domain which can translate the irq spec */
mutex_lock(&irq_domain_mutex);
list_for_each_entry(domain, &irq_domain_list, list) {
if (!domain->ops->dt_translate)
continue;
rc = domain->ops->dt_translate(domain, controller,
intspec, intsize, &hwirq, &type);
if (rc == 0)
break;
domain = controller ? irq_find_host(controller) : irq_default_domain;
if (!domain) {
#ifdef CONFIG_MIPS
/*
* Workaround to avoid breaking interrupt controller drivers
* that don't yet register an irq_domain. This is temporary
* code. ~~~gcl, Feb 24, 2012
*
* Scheduled for removal in Linux v3.6. That should be enough
* time.
*/
if (intsize > 0)
return intspec[0];
#endif
printk(KERN_WARNING "irq: no irq domain found for %s !\n",
controller->full_name);
return 0;
}
mutex_unlock(&irq_domain_mutex);
if (rc != 0)
/* If domain has no translation, then we assume interrupt line */
if (domain->ops->xlate == NULL)
hwirq = intspec[0];
else {
if (domain->ops->xlate(domain, controller, intspec, intsize,
&hwirq, &type))
return 0;
}
/* Create mapping */
virq = irq_create_mapping(domain, hwirq);
if (!virq)
return virq;
irq = irq_domain_to_irq(domain, hwirq);
if (type != IRQ_TYPE_NONE)
irq_set_irq_type(irq, type);
pr_debug("%s: mapped hwirq=%i to irq=%i, flags=%x\n",
controller->full_name, (int)hwirq, irq, type);
return irq;
/* Set type if specified and different than the current one */
if (type != IRQ_TYPE_NONE &&
type != (irqd_get_trigger_type(irq_get_irq_data(virq))))
irq_set_irq_type(virq, type);
return virq;
}
EXPORT_SYMBOL_GPL(irq_create_of_mapping);
/**
* irq_dispose_mapping() - Discard a mapping created by irq_create_of_mapping()
* @irq: linux irq number to be discarded
* irq_dispose_mapping() - Unmap an interrupt
* @virq: linux irq number of the interrupt to unmap
*/
void irq_dispose_mapping(unsigned int virq)
{
struct irq_data *irq_data = irq_get_irq_data(virq);
struct irq_domain *domain;
irq_hw_number_t hwirq;
if (!virq || !irq_data)
return;
domain = irq_data->domain;
if (WARN_ON(domain == NULL))
return;
/* Never unmap legacy interrupts */
if (domain->revmap_type == IRQ_DOMAIN_MAP_LEGACY)
return;
irq_set_status_flags(virq, IRQ_NOREQUEST);
/* remove chip and handler */
irq_set_chip_and_handler(virq, NULL, NULL);
/* Make sure it's completed */
synchronize_irq(virq);
/* Tell the PIC about it */
if (domain->ops->unmap)
domain->ops->unmap(domain, virq);
smp_mb();
/* Clear reverse map */
hwirq = irq_data->hwirq;
switch(domain->revmap_type) {
case IRQ_DOMAIN_MAP_LINEAR:
if (hwirq < domain->revmap_data.linear.size)
domain->revmap_data.linear.revmap[hwirq] = 0;
break;
case IRQ_DOMAIN_MAP_TREE:
mutex_lock(&revmap_trees_mutex);
radix_tree_delete(&domain->revmap_data.tree, hwirq);
mutex_unlock(&revmap_trees_mutex);
break;
}
irq_free_desc(virq);
}
EXPORT_SYMBOL_GPL(irq_dispose_mapping);
/**
* irq_find_mapping() - Find a linux irq from an hw irq number.
* @domain: domain owning this hardware interrupt
* @hwirq: hardware irq number in that domain space
*
* Calling this function indicates the caller no longer needs a reference to
* the linux irq number returned by a prior call to irq_create_of_mapping().
* This is a slow path, for use by generic code. It's expected that an
* irq controller implementation directly calls the appropriate low level
* mapping function.
*/
void irq_dispose_mapping(unsigned int irq)
unsigned int irq_find_mapping(struct irq_domain *domain,
irq_hw_number_t hwirq)
{
unsigned int i;
unsigned int hint = hwirq % irq_virq_count;
/* Look for default domain if nececssary */
if (domain == NULL)
domain = irq_default_domain;
if (domain == NULL)
return 0;
/* legacy -> bail early */
if (domain->revmap_type == IRQ_DOMAIN_MAP_LEGACY)
return irq_domain_legacy_revmap(domain, hwirq);
/* Slow path does a linear search of the map */
if (hint == 0)
hint = 1;
i = hint;
do {
struct irq_data *data = irq_get_irq_data(i);
if (data && (data->domain == domain) && (data->hwirq == hwirq))
return i;
i++;
if (i >= irq_virq_count)
i = 1;
} while(i != hint);
return 0;
}
EXPORT_SYMBOL_GPL(irq_find_mapping);
/**
* irq_radix_revmap_lookup() - Find a linux irq from a hw irq number.
* @domain: domain owning this hardware interrupt
* @hwirq: hardware irq number in that domain space
*
* This is a fast path, for use by irq controller code that uses radix tree
* revmaps
*/
unsigned int irq_radix_revmap_lookup(struct irq_domain *domain,
irq_hw_number_t hwirq)
{
struct irq_data *irq_data;
if (WARN_ON_ONCE(domain->revmap_type != IRQ_DOMAIN_MAP_TREE))
return irq_find_mapping(domain, hwirq);
/*
* nothing yet; will be filled when support for dynamic allocation of
* irq_descs is added to irq_domain
* Freeing an irq can delete nodes along the path to
* do the lookup via call_rcu.
*/
rcu_read_lock();
irq_data = radix_tree_lookup(&domain->revmap_data.tree, hwirq);
rcu_read_unlock();
/*
* If found in radix tree, then fine.
* Else fallback to linear lookup - this should not happen in practice
* as it means that we failed to insert the node in the radix tree.
*/
return irq_data ? irq_data->irq : irq_find_mapping(domain, hwirq);
}
EXPORT_SYMBOL_GPL(irq_dispose_mapping);
int irq_domain_simple_dt_translate(struct irq_domain *d,
struct device_node *controller,
/**
* irq_radix_revmap_insert() - Insert a hw irq to linux irq number mapping.
* @domain: domain owning this hardware interrupt
* @virq: linux irq number
* @hwirq: hardware irq number in that domain space
*
* This is for use by irq controllers that use a radix tree reverse
* mapping for fast lookup.
*/
void irq_radix_revmap_insert(struct irq_domain *domain, unsigned int virq,
irq_hw_number_t hwirq)
{
struct irq_data *irq_data = irq_get_irq_data(virq);
if (WARN_ON(domain->revmap_type != IRQ_DOMAIN_MAP_TREE))
return;
if (virq) {
mutex_lock(&revmap_trees_mutex);
radix_tree_insert(&domain->revmap_data.tree, hwirq, irq_data);
mutex_unlock(&revmap_trees_mutex);
}
}
/**
* irq_linear_revmap() - Find a linux irq from a hw irq number.
* @domain: domain owning this hardware interrupt
* @hwirq: hardware irq number in that domain space
*
* This is a fast path, for use by irq controller code that uses linear
* revmaps. It does fallback to the slow path if the revmap doesn't exist
* yet and will create the revmap entry with appropriate locking
*/
unsigned int irq_linear_revmap(struct irq_domain *domain,
irq_hw_number_t hwirq)
{
unsigned int *revmap;
if (WARN_ON_ONCE(domain->revmap_type != IRQ_DOMAIN_MAP_LINEAR))
return irq_find_mapping(domain, hwirq);
/* Check revmap bounds */
if (unlikely(hwirq >= domain->revmap_data.linear.size))
return irq_find_mapping(domain, hwirq);
/* Check if revmap was allocated */
revmap = domain->revmap_data.linear.revmap;
if (unlikely(revmap == NULL))
return irq_find_mapping(domain, hwirq);
/* Fill up revmap with slow path if no mapping found */
if (unlikely(!revmap[hwirq]))
revmap[hwirq] = irq_find_mapping(domain, hwirq);
return revmap[hwirq];
}
#ifdef CONFIG_VIRQ_DEBUG
static int virq_debug_show(struct seq_file *m, void *private)
{
unsigned long flags;
struct irq_desc *desc;
const char *p;
static const char none[] = "none";
void *data;
int i;
seq_printf(m, "%-5s %-7s %-15s %-18s %s\n", "virq", "hwirq",
"chip name", "chip data", "domain name");
for (i = 1; i < nr_irqs; i++) {
desc = irq_to_desc(i);
if (!desc)
continue;
raw_spin_lock_irqsave(&desc->lock, flags);
if (desc->action && desc->action->handler) {
struct irq_chip *chip;
seq_printf(m, "%5d ", i);
seq_printf(m, "0x%05lx ", desc->irq_data.hwirq);
chip = irq_desc_get_chip(desc);
if (chip && chip->name)
p = chip->name;
else
p = none;
seq_printf(m, "%-15s ", p);
data = irq_desc_get_chip_data(desc);
seq_printf(m, "0x%16p ", data);
if (desc->irq_data.domain->of_node)
p = desc->irq_data.domain->of_node->full_name;
else
p = none;
seq_printf(m, "%s\n", p);
}
raw_spin_unlock_irqrestore(&desc->lock, flags);
}
return 0;
}
static int virq_debug_open(struct inode *inode, struct file *file)
{
return single_open(file, virq_debug_show, inode->i_private);
}
static const struct file_operations virq_debug_fops = {
.open = virq_debug_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int __init irq_debugfs_init(void)
{
if (debugfs_create_file("virq_mapping", S_IRUGO, powerpc_debugfs_root,
NULL, &virq_debug_fops) == NULL)
return -ENOMEM;
return 0;
}
__initcall(irq_debugfs_init);
#endif /* CONFIG_VIRQ_DEBUG */
int irq_domain_simple_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hwirq)
{
return 0;
}
/**
* irq_domain_xlate_onecell() - Generic xlate for direct one cell bindings
*
* Device Tree IRQ specifier translation function which works with one cell
* bindings where the cell value maps directly to the hwirq number.
*/
int irq_domain_xlate_onecell(struct irq_domain *d, struct device_node *ctrlr,
const u32 *intspec, unsigned int intsize,
unsigned long *out_hwirq, unsigned int *out_type)
{
if (d->of_node != controller)
if (WARN_ON(intsize < 1))
return -EINVAL;
if (intsize < 1)
return -EINVAL;
if (d->nr_irq && ((intspec[0] < d->hwirq_base) ||
(intspec[0] >= d->hwirq_base + d->nr_irq)))
return -EINVAL;
*out_hwirq = intspec[0];
*out_type = IRQ_TYPE_NONE;
if (intsize > 1)
*out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
return 0;
}
EXPORT_SYMBOL_GPL(irq_domain_xlate_onecell);
/**
* irq_domain_create_simple() - Set up a 'simple' translation range
* irq_domain_xlate_twocell() - Generic xlate for direct two cell bindings
*
* Device Tree IRQ specifier translation function which works with two cell
* bindings where the cell values map directly to the hwirq number
* and linux irq flags.
*/
void irq_domain_add_simple(struct device_node *controller, int irq_base)
int irq_domain_xlate_twocell(struct irq_domain *d, struct device_node *ctrlr,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_type)
{
struct irq_domain *domain;
domain = kzalloc(sizeof(*domain), GFP_KERNEL);
if (!domain) {
WARN_ON(1);
return;
}
if (WARN_ON(intsize < 2))
return -EINVAL;
*out_hwirq = intspec[0];
*out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
return 0;
}
EXPORT_SYMBOL_GPL(irq_domain_xlate_twocell);
domain->irq_base = irq_base;
domain->of_node = of_node_get(controller);
domain->ops = &irq_domain_simple_ops;
irq_domain_add(domain);
/**
* irq_domain_xlate_onetwocell() - Generic xlate for one or two cell bindings
*
* Device Tree IRQ specifier translation function which works with either one
* or two cell bindings where the cell values map directly to the hwirq number
* and linux irq flags.
*
* Note: don't use this function unless your interrupt controller explicitly
* supports both one and two cell bindings. For the majority of controllers
* the _onecell() or _twocell() variants above should be used.
*/
int irq_domain_xlate_onetwocell(struct irq_domain *d,
struct device_node *ctrlr,
const u32 *intspec, unsigned int intsize,
unsigned long *out_hwirq, unsigned int *out_type)
{
if (WARN_ON(intsize < 1))
return -EINVAL;
*out_hwirq = intspec[0];
*out_type = (intsize > 1) ? intspec[1] : IRQ_TYPE_NONE;
return 0;
}
EXPORT_SYMBOL_GPL(irq_domain_add_simple);
EXPORT_SYMBOL_GPL(irq_domain_xlate_onetwocell);
const struct irq_domain_ops irq_domain_simple_ops = {
.map = irq_domain_simple_map,
.xlate = irq_domain_xlate_onetwocell,
};
EXPORT_SYMBOL_GPL(irq_domain_simple_ops);
#ifdef CONFIG_OF_IRQ
void irq_domain_generate_simple(const struct of_device_id *match,
u64 phys_base, unsigned int irq_start)
{
struct device_node *node;
pr_info("looking for phys_base=%llx, irq_start=%i\n",
pr_debug("looking for phys_base=%llx, irq_start=%i\n",
(unsigned long long) phys_base, (int) irq_start);
node = of_find_matching_node_by_address(NULL, match, phys_base);
if (node)
irq_domain_add_simple(node, irq_start);
else
pr_info("no node found\n");
irq_domain_add_legacy(node, 32, irq_start, 0,
&irq_domain_simple_ops, NULL);
}
EXPORT_SYMBOL_GPL(irq_domain_generate_simple);
#endif /* CONFIG_OF_IRQ */
struct irq_domain_ops irq_domain_simple_ops = {
#ifdef CONFIG_OF_IRQ
.dt_translate = irq_domain_simple_dt_translate,
#endif /* CONFIG_OF_IRQ */
};
EXPORT_SYMBOL_GPL(irq_domain_simple_ops);
#endif
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