x86/irq: Refine the way to allocate irq_cfg for legacy IRQs
[deliverable/linux.git] / arch / x86 / kernel / apic / io_apic.c
index f4dc2462a1ac410803cd94ff4944ebf23c636fa9..16d4ba3ac844d44a0eac2692f79650a14657bcec 100644 (file)
@@ -78,6 +78,13 @@ static DEFINE_MUTEX(ioapic_mutex);
 static unsigned int ioapic_dynirq_base;
 static int ioapic_initialized;
 
+struct mp_chip_data {
+       struct IO_APIC_route_entry entry;
+       int trigger;
+       int polarity;
+       bool isa_irq;
+};
+
 struct mp_pin_info {
        int trigger;
        int polarity;
@@ -247,8 +254,7 @@ static void free_ioapic_saved_registers(int idx)
 
 int __init arch_early_ioapic_init(void)
 {
-       struct irq_cfg *cfg;
-       int i, node = cpu_to_node(0);
+       int i;
 
        if (!nr_legacy_irqs())
                io_apic_irqs = ~0UL;
@@ -256,16 +262,6 @@ int __init arch_early_ioapic_init(void)
        for_each_ioapic(i)
                alloc_ioapic_saved_registers(i);
 
-       /*
-        * For legacy IRQ's, start with assigning irq0 to irq15 to
-        * IRQ0_VECTOR to IRQ15_VECTOR for all cpu's.
-        */
-       for (i = 0; i < nr_legacy_irqs(); i++) {
-               cfg = alloc_irq_and_cfg_at(i, node);
-               cfg->vector = IRQ0_VECTOR + i;
-               cpumask_setall(cfg->domain);
-       }
-
        return 0;
 }
 
@@ -938,10 +934,39 @@ static int irq_trigger(int idx)
        return trigger;
 }
 
-static int alloc_irq_from_domain(struct irq_domain *domain, u32 gsi, int pin)
+void ioapic_set_alloc_attr(struct irq_alloc_info *info, int node,
+                          int trigger, int polarity)
+{
+       init_irq_alloc_info(info, NULL);
+       info->type = X86_IRQ_ALLOC_TYPE_IOAPIC;
+       info->ioapic_node = node;
+       info->ioapic_trigger = trigger;
+       info->ioapic_polarity = polarity;
+       info->ioapic_valid = 1;
+}
+
+static void mp_register_handler(unsigned int irq, unsigned long trigger)
+{
+       irq_flow_handler_t hdl;
+       bool fasteoi;
+
+       if (trigger) {
+               irq_set_status_flags(irq, IRQ_LEVEL);
+               fasteoi = true;
+       } else {
+               irq_clear_status_flags(irq, IRQ_LEVEL);
+               fasteoi = false;
+       }
+
+       hdl = fasteoi ? handle_fasteoi_irq : handle_edge_irq;
+       __irq_set_handler(irq, hdl, 0, fasteoi ? "fasteoi" : "edge");
+}
+
+static int alloc_irq_from_domain(struct irq_domain *domain, u32 gsi, int pin,
+                                struct irq_alloc_info *info)
 {
        int irq = -1;
-       int ioapic = (int)(long)domain->host_data;
+       int ioapic = mp_irqdomain_ioapic_idx(domain);
        int type = ioapics[ioapic].irqdomain_cfg.type;
 
        switch (type) {
@@ -971,11 +996,11 @@ static int alloc_irq_from_domain(struct irq_domain *domain, u32 gsi, int pin)
 }
 
 static int mp_map_pin_to_irq(u32 gsi, int idx, int ioapic, int pin,
-                            unsigned int flags)
+                            unsigned int flags, struct irq_alloc_info *info)
 {
        int irq;
        struct irq_domain *domain = mp_ioapic_irqdomain(ioapic);
-       struct mp_pin_info *info = mp_pin_info(ioapic, pin);
+       struct mp_pin_info *pinfo = mp_pin_info(ioapic, pin);
 
        if (!domain)
                return -1;
@@ -997,30 +1022,30 @@ static int mp_map_pin_to_irq(u32 gsi, int idx, int ioapic, int pin,
        if (idx >= 0 && test_bit(mp_irqs[idx].srcbus, mp_bus_not_pci)) {
                irq = mp_irqs[idx].srcbusirq;
                if (flags & IOAPIC_MAP_ALLOC) {
-                       if (info->count == 0 &&
+                       if (pinfo->count == 0 &&
                            mp_irqdomain_map(domain, irq, pin) != 0)
                                irq = -1;
 
                        /* special handling for timer IRQ0 */
                        if (irq == 0)
-                               info->count++;
+                               pinfo->count++;
                }
        } else {
                irq = irq_find_mapping(domain, pin);
                if (irq <= 0 && (flags & IOAPIC_MAP_ALLOC))
-                       irq = alloc_irq_from_domain(domain, gsi, pin);
+                       irq = alloc_irq_from_domain(domain, gsi, pin, info);
        }
 
        if (flags & IOAPIC_MAP_ALLOC) {
                /* special handling for legacy IRQs */
-               if (irq < nr_legacy_irqs() && info->count == 1 &&
+               if (irq < nr_legacy_irqs() && pinfo->count == 1 &&
                    mp_irqdomain_map(domain, irq, pin) != 0)
                        irq = -1;
 
                if (irq > 0)
-                       info->count++;
-               else if (info->count == 0)
-                       info->set = 0;
+                       pinfo->count++;
+               else if (pinfo->count == 0)
+                       pinfo->set = 0;
        }
 
        mutex_unlock(&ioapic_mutex);
@@ -1058,10 +1083,11 @@ static int pin_2_irq(int idx, int ioapic, int pin, unsigned int flags)
        }
 #endif
 
-       return  mp_map_pin_to_irq(gsi, idx, ioapic, pin, flags);
+       return  mp_map_pin_to_irq(gsi, idx, ioapic, pin, flags, NULL);
 }
 
-int mp_map_gsi_to_irq(u32 gsi, unsigned int flags)
+int mp_map_gsi_to_irq(u32 gsi, unsigned int flags,
+                     struct irq_alloc_info *info)
 {
        int ioapic, pin, idx;
 
@@ -1074,7 +1100,7 @@ int mp_map_gsi_to_irq(u32 gsi, unsigned int flags)
        if ((flags & IOAPIC_MAP_CHECK) && idx < 0)
                return -1;
 
-       return mp_map_pin_to_irq(gsi, idx, ioapic, pin, flags);
+       return mp_map_pin_to_irq(gsi, idx, ioapic, pin, flags, info);
 }
 
 void mp_unmap_irq(int irq)
@@ -2356,9 +2382,6 @@ static int mp_irqdomain_create(int ioapic)
                ioapic_dynirq_base = max(ioapic_dynirq_base,
                                         gsi_cfg->gsi_end + 1);
 
-       if (gsi_cfg->gsi_base == 0)
-               irq_set_default_host(ip->irqdomain);
-
        return 0;
 }
 
@@ -3019,7 +3042,7 @@ static inline void set_io_apic_irq_attr(struct io_apic_irq_attr *irq_attr,
 int mp_irqdomain_map(struct irq_domain *domain, unsigned int virq,
                     irq_hw_number_t hwirq)
 {
-       int ioapic = (int)(long)domain->host_data;
+       int ioapic = mp_irqdomain_ioapic_idx(domain);
        struct mp_pin_info *info = mp_pin_info(ioapic, hwirq);
        struct io_apic_irq_attr attr;
 
@@ -3057,7 +3080,7 @@ void mp_irqdomain_unmap(struct irq_domain *domain, unsigned int virq)
 {
        struct irq_data *data = irq_get_irq_data(virq);
        struct irq_cfg *cfg = irq_cfg(virq);
-       int ioapic = (int)(long)domain->host_data;
+       int ioapic = mp_irqdomain_ioapic_idx(domain);
        int pin = (int)data->hwirq;
 
        ioapic_mask_entry(ioapic, pin);
@@ -3066,6 +3089,130 @@ void mp_irqdomain_unmap(struct irq_domain *domain, unsigned int virq)
        arch_teardown_hwirq(virq);
 }
 
+static void mp_irqdomain_get_attr(u32 gsi, struct mp_chip_data *data,
+                                struct irq_alloc_info *info)
+{
+       if (info && info->ioapic_valid) {
+               data->trigger = info->ioapic_trigger;
+               data->polarity = info->ioapic_polarity;
+       } else if (acpi_get_override_irq(gsi, &data->trigger,
+                                        &data->polarity) < 0) {
+               /* PCI interrupts are always polarity one level triggered. */
+               data->trigger = 1;
+               data->polarity = 1;
+       }
+}
+
+static void mp_setup_entry(struct irq_cfg *cfg, struct mp_chip_data *data,
+                          struct IO_APIC_route_entry *entry)
+{
+       memset(entry, 0, sizeof(*entry));
+       entry->delivery_mode = apic->irq_delivery_mode;
+       entry->dest_mode     = apic->irq_dest_mode;
+       entry->dest          = cfg->dest_apicid;
+       entry->vector        = cfg->vector;
+       entry->mask          = 0;       /* enable IRQ */
+       entry->trigger       = data->trigger;
+       entry->polarity      = data->polarity;
+       /*
+        * Mask level triggered irqs.
+        * Use IRQ_DELAYED_DISABLE for edge triggered irqs.
+        */
+       if (data->trigger)
+               entry->mask = 1;
+}
+
+int mp_irqdomain_alloc(struct irq_domain *domain, unsigned int virq,
+                      unsigned int nr_irqs, void *arg)
+{
+       int ret, ioapic, pin;
+       struct irq_cfg *cfg;
+       struct irq_data *irq_data;
+       struct mp_chip_data *data;
+       struct irq_alloc_info *info = arg;
+
+       if (!info || nr_irqs > 1)
+               return -EINVAL;
+       irq_data = irq_domain_get_irq_data(domain, virq);
+       if (!irq_data)
+               return -EINVAL;
+
+       ioapic = mp_irqdomain_ioapic_idx(domain);
+       pin = info->ioapic_pin;
+       if (irq_find_mapping(domain, (irq_hw_number_t)pin) > 0)
+               return -EEXIST;
+
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       info->ioapic_entry = &data->entry;
+       ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, info);
+       if (ret < 0) {
+               kfree(data);
+               return ret;
+       }
+
+       irq_data->hwirq = info->ioapic_pin;
+       irq_data->chip = &ioapic_chip;
+       irq_data->chip_data = data;
+       mp_irqdomain_get_attr(mp_pin_to_gsi(ioapic, pin), data, info);
+
+       cfg = irqd_cfg(irq_data);
+       add_pin_to_irq_node(cfg, info->ioapic_node, ioapic, pin);
+       if (info->ioapic_entry)
+               mp_setup_entry(cfg, data, info->ioapic_entry);
+       mp_register_handler(virq, data->trigger);
+       if (virq < nr_legacy_irqs())
+               legacy_pic->mask(virq);
+
+       apic_printk(APIC_VERBOSE, KERN_DEBUG
+                   "IOAPIC[%d]: Set routing entry (%d-%d -> 0x%x -> IRQ %d Mode:%i Active:%i Dest:%d)\n",
+                   ioapic, mpc_ioapic_id(ioapic), pin, cfg->vector,
+                   virq, data->trigger, data->polarity, cfg->dest_apicid);
+
+       return 0;
+}
+
+void mp_irqdomain_free(struct irq_domain *domain, unsigned int virq,
+                      unsigned int nr_irqs)
+{
+       struct irq_cfg *cfg = irq_cfg(virq);
+       struct irq_data *irq_data;
+
+       BUG_ON(nr_irqs != 1);
+       irq_data = irq_domain_get_irq_data(domain, virq);
+       if (irq_data && irq_data->chip_data) {
+               __remove_pin_from_irq(cfg, mp_irqdomain_ioapic_idx(domain),
+                                     (int)irq_data->hwirq);
+               WARN_ON(!list_empty(&cfg->irq_2_pin));
+               kfree(irq_data->chip_data);
+       }
+       irq_domain_free_irqs_top(domain, virq, nr_irqs);
+}
+
+void mp_irqdomain_activate(struct irq_domain *domain,
+                          struct irq_data *irq_data)
+{
+       unsigned long flags;
+       struct irq_pin_list *entry;
+       struct mp_chip_data *data = irq_data->chip_data;
+       struct irq_cfg *cfg = irqd_cfg(irq_data);
+
+       raw_spin_lock_irqsave(&ioapic_lock, flags);
+       for_each_irq_pin(entry, cfg->irq_2_pin)
+               __ioapic_write_entry(entry->apic, entry->pin, data->entry);
+       raw_spin_unlock_irqrestore(&ioapic_lock, flags);
+}
+
+void mp_irqdomain_deactivate(struct irq_domain *domain,
+                            struct irq_data *irq_data)
+{
+       /* It won't be called for IRQ with multiple IOAPIC pins associated */
+       ioapic_mask_entry(mp_irqdomain_ioapic_idx(domain),
+                         (int)irq_data->hwirq);
+}
+
 int mp_set_gsi_attr(u32 gsi, int trigger, int polarity, int node)
 {
        int ret = 0;
@@ -3095,19 +3242,7 @@ int mp_set_gsi_attr(u32 gsi, int trigger, int polarity, int node)
        return ret;
 }
 
-/* Enable IOAPIC early just for system timer */
-void __init pre_init_apic_IRQ0(void)
+int mp_irqdomain_ioapic_idx(struct irq_domain *domain)
 {
-       struct io_apic_irq_attr attr = { 0, 0, 0, 0 };
-
-       printk(KERN_INFO "Early APIC setup for system timer0\n");
-#ifndef CONFIG_SMP
-       physid_set_mask_of_physid(boot_cpu_physical_apicid,
-                                        &phys_cpu_present_map);
-#endif
-       setup_local_APIC();
-
-       io_apic_setup_irq_pin(0, 0, &attr);
-       irq_set_chip_and_handler_name(0, &ioapic_chip, handle_edge_irq,
-                                     "edge");
+       return (int)(long)domain->host_data;
 }
This page took 0.031461 seconds and 5 git commands to generate.