Merge tag 'iwlwifi-next-for-kalle-2014-12-30' of https://git.kernel.org/pub/scm/linux...
[cascardo/linux.git] / arch / x86 / kernel / apic / htirq.c
1 /*
2  * Support Hypertransport IRQ
3  *
4  * Copyright (C) 1997, 1998, 1999, 2000, 2009 Ingo Molnar, Hajnalka Szabo
5  *      Moved from arch/x86/kernel/apic/io_apic.c.
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  */
11 #include <linux/mm.h>
12 #include <linux/interrupt.h>
13 #include <linux/init.h>
14 #include <linux/device.h>
15 #include <linux/pci.h>
16 #include <linux/htirq.h>
17 #include <asm/hw_irq.h>
18 #include <asm/apic.h>
19 #include <asm/hypertransport.h>
20
21 /*
22  * Hypertransport interrupt support
23  */
24 static void target_ht_irq(unsigned int irq, unsigned int dest, u8 vector)
25 {
26         struct ht_irq_msg msg;
27
28         fetch_ht_irq_msg(irq, &msg);
29
30         msg.address_lo &= ~(HT_IRQ_LOW_VECTOR_MASK | HT_IRQ_LOW_DEST_ID_MASK);
31         msg.address_hi &= ~(HT_IRQ_HIGH_DEST_ID_MASK);
32
33         msg.address_lo |= HT_IRQ_LOW_VECTOR(vector) | HT_IRQ_LOW_DEST_ID(dest);
34         msg.address_hi |= HT_IRQ_HIGH_DEST_ID(dest);
35
36         write_ht_irq_msg(irq, &msg);
37 }
38
39 static int
40 ht_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force)
41 {
42         struct irq_cfg *cfg = irqd_cfg(data);
43         unsigned int dest;
44         int ret;
45
46         ret = apic_set_affinity(data, mask, &dest);
47         if (ret)
48                 return ret;
49
50         target_ht_irq(data->irq, dest, cfg->vector);
51         return IRQ_SET_MASK_OK_NOCOPY;
52 }
53
54 static struct irq_chip ht_irq_chip = {
55         .name                   = "PCI-HT",
56         .irq_mask               = mask_ht_irq,
57         .irq_unmask             = unmask_ht_irq,
58         .irq_ack                = apic_ack_edge,
59         .irq_set_affinity       = ht_set_affinity,
60         .irq_retrigger          = apic_retrigger_irq,
61         .flags                  = IRQCHIP_SKIP_SET_WAKE,
62 };
63
64 int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev)
65 {
66         struct irq_cfg *cfg;
67         struct ht_irq_msg msg;
68         unsigned dest;
69         int err;
70
71         if (disable_apic)
72                 return -ENXIO;
73
74         cfg = irq_cfg(irq);
75         err = assign_irq_vector(irq, cfg, apic->target_cpus());
76         if (err)
77                 return err;
78
79         err = apic->cpu_mask_to_apicid_and(cfg->domain,
80                                            apic->target_cpus(), &dest);
81         if (err)
82                 return err;
83
84         msg.address_hi = HT_IRQ_HIGH_DEST_ID(dest);
85
86         msg.address_lo =
87                 HT_IRQ_LOW_BASE |
88                 HT_IRQ_LOW_DEST_ID(dest) |
89                 HT_IRQ_LOW_VECTOR(cfg->vector) |
90                 ((apic->irq_dest_mode == 0) ?
91                         HT_IRQ_LOW_DM_PHYSICAL :
92                         HT_IRQ_LOW_DM_LOGICAL) |
93                 HT_IRQ_LOW_RQEOI_EDGE |
94                 ((apic->irq_delivery_mode != dest_LowestPrio) ?
95                         HT_IRQ_LOW_MT_FIXED :
96                         HT_IRQ_LOW_MT_ARBITRATED) |
97                 HT_IRQ_LOW_IRQ_MASKED;
98
99         write_ht_irq_msg(irq, &msg);
100
101         irq_set_chip_and_handler_name(irq, &ht_irq_chip,
102                                       handle_edge_irq, "edge");
103
104         dev_dbg(&dev->dev, "irq %d for HT\n", irq);
105
106         return 0;
107 }