[PATCH 09/11] x86/ioapic: Add OF bindings for IO-APIC

Grant Likely grant.likely at secretlab.ca
Wed Feb 23 08:14:08 EST 2011


On Tue, Feb 22, 2011 at 1:07 PM, Sebastian Andrzej Siewior
<bigeasy at linutronix.de> wrote:
> ioapic_xlate provides a translation from the information in device tree
> to ioapic related informations. This includes
> - obtaining hw irq which is the vector number "=> pin number + gsi"
> - obtaining type (level/edge/..)
> - programming this information into ioapic
>
> ioapic_add_ofnode adds an irq_domain based on informations from the device
> tree. This information (irq_domain) is required in order to map a device to
> its proper interrupt controller.
>
> Cc: devicetree-discuss at lists.ozlabs.org
> Signed-off-by: Sebastian Andrzej Siewior <bigeasy at linutronix.de>
> Signed-off-by: Dirk Brandewie <dirk.brandewie at gmail.com>
> ---
>  arch/x86/include/asm/io_apic.h |    7 +++
>  arch/x86/include/asm/prom.h    |    2 +
>  arch/x86/kernel/apic/io_apic.c |   99 ++++++++++++++++++++++++++++++++++++++++
>  arch/x86/kernel/devicetree.c   |   13 +++++
>  arch/x86/kernel/irqinit.c      |    6 ++
>  5 files changed, 127 insertions(+), 0 deletions(-)
>
> diff --git a/arch/x86/include/asm/io_apic.h b/arch/x86/include/asm/io_apic.h
> index f327d38..fe88a73 100644
> --- a/arch/x86/include/asm/io_apic.h
> +++ b/arch/x86/include/asm/io_apic.h
> @@ -177,6 +177,13 @@ struct mp_ioapic_gsi{
>        u32 gsi_base;
>        u32 gsi_end;
>  };
> +#ifdef CONFIG_OF
> +struct mp_of_ioapic {
> +       struct device_node *node;
> +};
> +extern struct mp_of_ioapic mp_of_ioapic[MAX_IO_APICS];
> +void __init ioapic_add_ofnode(struct device_node *np);
> +#endif
>  extern struct mp_ioapic_gsi  mp_gsi_routing[];
>  extern u32 gsi_top;
>  int mp_find_ioapic(u32 gsi);
> diff --git a/arch/x86/include/asm/prom.h b/arch/x86/include/asm/prom.h
> index 3e441b3..98b9a73 100644
> --- a/arch/x86/include/asm/prom.h
> +++ b/arch/x86/include/asm/prom.h
> @@ -26,6 +26,7 @@
>  extern int of_ioapic;
>  extern u64 initial_dtb;
>  extern void add_dtb(u64 data);
> +extern void x86_add_irq_domains(void);
>  void x86_dtb_find_config(void);
>  void x86_dtb_get_config(unsigned int unused);
>  void add_interrupt_host(struct irq_domain *ih);
> @@ -43,6 +44,7 @@ static inline struct device_node *pci_bus_to_OF_node(struct pci_bus *bus)
>
>  #else
>  static inline void add_dtb(u64 data) { }
> +static inline void x86_add_irq_domains(void) { }
>  static inline void x86_of_pci_init(void) { }
>  #define x86_dtb_find_config x86_init_noop
>  #define x86_dtb_get_config x86_init_uint_noop
> diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c
> index ca9e2a3..c172452 100644
> --- a/arch/x86/kernel/apic/io_apic.c
> +++ b/arch/x86/kernel/apic/io_apic.c
> @@ -43,6 +43,7 @@
>  #include <linux/bootmem.h>
>  #include <linux/dmar.h>
>  #include <linux/hpet.h>
> +#include <linux/of_address.h>
>
>  #include <asm/idle.h>
>  #include <asm/io.h>
> @@ -60,6 +61,7 @@
>  #include <asm/irq_remapping.h>
>  #include <asm/hpet.h>
>  #include <asm/hw_irq.h>
> +#include <asm/irq_controller.h>
>
>  #include <asm/apic.h>
>
> @@ -88,6 +90,10 @@ int nr_ioapics;
>  /* IO APIC gsi routing info */
>  struct mp_ioapic_gsi  mp_gsi_routing[MAX_IO_APICS];
>
> +#ifdef CONFIG_OF
> +/* OF -> IO APIC lookup */
> +struct mp_of_ioapic mp_of_ioapic[MAX_IO_APICS];
> +#endif
>  /* The one past the highest gsi number used */
>  u32 gsi_top;
>
> @@ -4083,6 +4089,99 @@ void __init mp_register_ioapic(int id, u32 address, u32 gsi_base)
>        nr_ioapics++;
>  }
>
> +#ifdef CONFIG_OF
> +static int ioapic_xlate(struct irq_domain *id, const u32 *intspec, u32 intsize,
> +               u32 *out_hwirq, u32 *out_type)
> +{
> +       u32 line;
> +       u32 idx;
> +       u32 type;
> +       u32 trigger;
> +       u32 polarity;
> +       struct irq_cfg *cfg;
> +       struct irq_desc *desc;
> +
> +       if (intsize < 1)
> +               return -EINVAL;
> +
> +       line = *intspec;
> +       idx = (u32) id->priv;
> +       *out_hwirq = line + mp_gsi_routing[idx].gsi_base;
> +       if (intsize > 1) {
> +               intspec++;
> +               type = *intspec;
> +               switch (type) {
> +               case 0:
> +                       *out_type = IRQ_TYPE_EDGE_RISING;
> +                       trigger = IOAPIC_EDGE;
> +                       polarity = 1;
> +                       break;
> +               case 1:
> +                       *out_type = IRQ_TYPE_LEVEL_LOW;
> +                       trigger = IOAPIC_LEVEL;
> +                       polarity = 0;
> +                       break;
> +               case 2:
> +                       *out_type = IRQ_TYPE_LEVEL_HIGH;
> +                       trigger = IOAPIC_LEVEL;
> +                       polarity = 1;
> +                       break;
> +               case 3:
> +                       *out_type = IRQ_TYPE_EDGE_FALLING;
> +                       trigger = IOAPIC_EDGE;
> +                       polarity = 0;
> +                       break;

This seems to beg for a lookup table.  :-)

But that can be changed later if you agree.

> +               default:
> +                       *out_type = IRQ_TYPE_NONE;
> +                       trigger = IOAPIC_AUTO;
> +                       polarity = 0;
> +                       break;
> +               };
> +       } else {
> +               *out_type = IRQ_TYPE_NONE;
> +               trigger = IOAPIC_AUTO;
> +               polarity = 0;
> +       }

Actually, if the irq specifier is invalid (both for the default case,
and the case where the size is too small), then this function should
flat out refuse to xlate the irq and complain loudly.  Force users to
provide good data.  Again, this can be fixed up with a followon patch.

> +       /* And now tell the IO APIC to make the line ready */
> +       desc = irq_to_desc_alloc_node(*out_hwirq, 0);
> +       cfg = irq_cfg(*out_hwirq);
> +       add_pin_to_irq_node(cfg, 0, idx, line);
> +       /* make it edge by default, settype will update it */
> +       setup_ioapic_irq(idx, line, *out_hwirq, cfg, trigger, polarity);
> +       return 0;
> +}
> +
> +void __init ioapic_add_ofnode(struct device_node *np)
> +{
> +       int i;
> +       int ret;
> +       struct resource r;
> +
> +       ret = of_address_to_resource(np, 0, &r);
> +       if (ret) {
> +               printk(KERN_ERR "Failed to obtain address for %s\n",
> +                               np->full_name);
> +               return;
> +       }
> +
> +       for (i = 0; i < nr_ioapics; i++) {
> +               if (r.start == mp_ioapics[i].apicaddr) {
> +                       struct irq_domain *id;
> +
> +                       mp_of_ioapic[i].node = np;
> +                       id = kzalloc(sizeof(*id), GFP_KERNEL);
> +                       BUG_ON(!id);
> +                       id->controller = np;
> +                       id->xlate = ioapic_xlate;
> +                       id->priv = (void *)i;
> +                       add_interrupt_host(id);
> +                       return;
> +               }
> +       }
> +       printk(KERN_ERR "IOxAPIC at %s is not registered.\n", np->full_name);
> +}
> +#endif
> +
>  /* Enable IOAPIC early just for system timer */
>  void __init pre_init_apic_IRQ0(void)
>  {
> diff --git a/arch/x86/kernel/devicetree.c b/arch/x86/kernel/devicetree.c
> index 179833f..62d0072 100644
> --- a/arch/x86/kernel/devicetree.c
> +++ b/arch/x86/kernel/devicetree.c
> @@ -322,3 +322,16 @@ void __init x86_dtb_get_config(unsigned int unused)
>        dtb_setup_hpet();
>        dtb_apic_setup();
>  }
> +
> +void __init x86_add_irq_domains(void)
> +{
> +       struct device_node *dp;
> +
> +       if (!initial_boot_params)
> +               return;
> +
> +       for_each_node_with_property(dp, "interrupt-controller") {
> +               if (of_device_is_compatible(dp, "intel,ce4100-ioapic"))
> +                       ioapic_add_ofnode(dp);
> +       }
> +}
> diff --git a/arch/x86/kernel/irqinit.c b/arch/x86/kernel/irqinit.c
> index 4cadf86..9f76f89 100644
> --- a/arch/x86/kernel/irqinit.c
> +++ b/arch/x86/kernel/irqinit.c
> @@ -119,6 +119,12 @@ void __init init_IRQ(void)
>        int i;
>
>        /*
> +        * We probably need a better place for this, but it works for
> +        * now ...
> +        */
> +       x86_add_irq_domains();
> +
> +       /*
>         * On cpu 0, Assign IRQ0_VECTOR..IRQ15_VECTOR's to IRQ 0..15.
>         * If these IRQ's are handled by legacy interrupt-controllers like PIC,
>         * then this configuration will likely be static after the boot. If
> --
> 1.7.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
>



-- 
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.


More information about the devicetree-discuss mailing list