mvebu: backport mainline patches from kernel 3.12
[openwrt.git] / target / linux / mvebu / patches-3.10 / 0075-irqchip-armada-370-xp-implement-MSI-support.patch
1 From eaa70d53f6b827f147d775a3de7ff3ef27d0fae6 Mon Sep 17 00:00:00 2001
2 From: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
3 Date: Thu, 6 Jun 2013 18:25:16 +0200
4 Subject: [PATCH 075/203] irqchip: armada-370-xp: implement MSI support
5
6 This commit introduces the support for the MSI interrupts in the
7 armada-370-xp interrupt controller driver. It registers an MSI chip to
8 the MSI chip registry, which will be used by the Marvell PCIe host
9 controller driver.
10
11 The MSI interrupts use the 16 high doorbells, and are therefore
12 notified using IRQ1 of the main interrupt controller.
13
14 Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
15 Acked-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
16 ---
17  .../devicetree/bindings/arm/armada-370-xp-mpic.txt |   3 +
18  drivers/irqchip/irq-armada-370-xp.c                | 182 ++++++++++++++++++++-
19  2 files changed, 184 insertions(+), 1 deletion(-)
20
21 --- a/Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt
22 +++ b/Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt
23 @@ -4,6 +4,8 @@ Marvell Armada 370 and Armada XP Interru
24  Required properties:
25  - compatible: Should be "marvell,mpic"
26  - interrupt-controller: Identifies the node as an interrupt controller.
27 +- msi-controller: Identifies the node as an PCI Message Signaled
28 +  Interrupt controller.
29  - #interrupt-cells: The number of cells to define the interrupts. Should be 1.
30    The cell is the IRQ number
31  
32 @@ -24,6 +26,7 @@ Example:
33                #address-cells = <1>;
34                #size-cells = <1>;
35                interrupt-controller;
36 +              msi-controller;
37                reg = <0xd0020a00 0x1d0>,
38                      <0xd0021070 0x58>;
39          };
40 --- a/drivers/irqchip/irq-armada-370-xp.c
41 +++ b/drivers/irqchip/irq-armada-370-xp.c
42 @@ -21,7 +21,10 @@
43  #include <linux/io.h>
44  #include <linux/of_address.h>
45  #include <linux/of_irq.h>
46 +#include <linux/of_pci.h>
47  #include <linux/irqdomain.h>
48 +#include <linux/slab.h>
49 +#include <linux/msi.h>
50  #include <asm/mach/arch.h>
51  #include <asm/exception.h>
52  #include <asm/smp_plat.h>
53 @@ -51,12 +54,22 @@
54  #define IPI_DOORBELL_START                      (0)
55  #define IPI_DOORBELL_END                        (8)
56  #define IPI_DOORBELL_MASK                       0xFF
57 +#define PCI_MSI_DOORBELL_START                  (16)
58 +#define PCI_MSI_DOORBELL_NR                     (16)
59 +#define PCI_MSI_DOORBELL_END                    (32)
60 +#define PCI_MSI_DOORBELL_MASK                   0xFFFF0000
61  
62  static DEFINE_RAW_SPINLOCK(irq_controller_lock);
63  
64  static void __iomem *per_cpu_int_base;
65  static void __iomem *main_int_base;
66  static struct irq_domain *armada_370_xp_mpic_domain;
67 +#ifdef CONFIG_PCI_MSI
68 +static struct irq_domain *armada_370_xp_msi_domain;
69 +static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR);
70 +static DEFINE_MUTEX(msi_used_lock);
71 +static phys_addr_t msi_doorbell_addr;
72 +#endif
73  
74  /*
75   * In SMP mode:
76 @@ -87,6 +100,144 @@ static void armada_370_xp_irq_unmask(str
77                                 ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
78  }
79  
80 +#ifdef CONFIG_PCI_MSI
81 +
82 +static int armada_370_xp_alloc_msi(void)
83 +{
84 +       int hwirq;
85 +
86 +       mutex_lock(&msi_used_lock);
87 +       hwirq = find_first_zero_bit(&msi_used, PCI_MSI_DOORBELL_NR);
88 +       if (hwirq >= PCI_MSI_DOORBELL_NR)
89 +               hwirq = -ENOSPC;
90 +       else
91 +               set_bit(hwirq, msi_used);
92 +       mutex_unlock(&msi_used_lock);
93 +
94 +       return hwirq;
95 +}
96 +
97 +static void armada_370_xp_free_msi(int hwirq)
98 +{
99 +       mutex_lock(&msi_used_lock);
100 +       if (!test_bit(hwirq, msi_used))
101 +               pr_err("trying to free unused MSI#%d\n", hwirq);
102 +       else
103 +               clear_bit(hwirq, msi_used);
104 +       mutex_unlock(&msi_used_lock);
105 +}
106 +
107 +static int armada_370_xp_setup_msi_irq(struct msi_chip *chip,
108 +                                      struct pci_dev *pdev,
109 +                                      struct msi_desc *desc)
110 +{
111 +       struct msi_msg msg;
112 +       irq_hw_number_t hwirq;
113 +       int virq;
114 +
115 +       hwirq = armada_370_xp_alloc_msi();
116 +       if (hwirq < 0)
117 +               return hwirq;
118 +
119 +       virq = irq_create_mapping(armada_370_xp_msi_domain, hwirq);
120 +       if (!virq) {
121 +               armada_370_xp_free_msi(hwirq);
122 +               return -EINVAL;
123 +       }
124 +
125 +       irq_set_msi_desc(virq, desc);
126 +
127 +       msg.address_lo = msi_doorbell_addr;
128 +       msg.address_hi = 0;
129 +       msg.data = 0xf00 | (hwirq + 16);
130 +
131 +       write_msi_msg(virq, &msg);
132 +       return 0;
133 +}
134 +
135 +static void armada_370_xp_teardown_msi_irq(struct msi_chip *chip,
136 +                                          unsigned int irq)
137 +{
138 +       struct irq_data *d = irq_get_irq_data(irq);
139 +       irq_dispose_mapping(irq);
140 +       armada_370_xp_free_msi(d->hwirq);
141 +}
142 +
143 +static struct irq_chip armada_370_xp_msi_irq_chip = {
144 +       .name = "armada_370_xp_msi_irq",
145 +       .irq_enable = unmask_msi_irq,
146 +       .irq_disable = mask_msi_irq,
147 +       .irq_mask = mask_msi_irq,
148 +       .irq_unmask = unmask_msi_irq,
149 +};
150 +
151 +static int armada_370_xp_msi_map(struct irq_domain *domain, unsigned int virq,
152 +                                irq_hw_number_t hw)
153 +{
154 +       irq_set_chip_and_handler(virq, &armada_370_xp_msi_irq_chip,
155 +                                handle_simple_irq);
156 +       set_irq_flags(virq, IRQF_VALID);
157 +
158 +       return 0;
159 +}
160 +
161 +static const struct irq_domain_ops armada_370_xp_msi_irq_ops = {
162 +       .map = armada_370_xp_msi_map,
163 +};
164 +
165 +static int armada_370_xp_msi_init(struct device_node *node,
166 +                                 phys_addr_t main_int_phys_base)
167 +{
168 +       struct msi_chip *msi_chip;
169 +       u32 reg;
170 +       int ret;
171 +
172 +       msi_doorbell_addr = main_int_phys_base +
173 +               ARMADA_370_XP_SW_TRIG_INT_OFFS;
174 +
175 +       msi_chip = kzalloc(sizeof(*msi_chip), GFP_KERNEL);
176 +       if (!msi_chip)
177 +               return -ENOMEM;
178 +
179 +       msi_chip->setup_irq = armada_370_xp_setup_msi_irq;
180 +       msi_chip->teardown_irq = armada_370_xp_teardown_msi_irq;
181 +       msi_chip->of_node = node;
182 +
183 +       armada_370_xp_msi_domain =
184 +               irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR,
185 +                                     &armada_370_xp_msi_irq_ops,
186 +                                     NULL);
187 +       if (!armada_370_xp_msi_domain) {
188 +               kfree(msi_chip);
189 +               return -ENOMEM;
190 +       }
191 +
192 +       ret = of_pci_msi_chip_add(msi_chip);
193 +       if (ret < 0) {
194 +               irq_domain_remove(armada_370_xp_msi_domain);
195 +               kfree(msi_chip);
196 +               return ret;
197 +       }
198 +
199 +       reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS)
200 +               | PCI_MSI_DOORBELL_MASK;
201 +
202 +       writel(reg, per_cpu_int_base +
203 +              ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
204 +
205 +       /* Unmask IPI interrupt */
206 +       writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
207 +
208 +       return 0;
209 +}
210 +#else
211 +static inline int armada_370_xp_msi_init(struct device_node *node,
212 +                                        phys_addr_t main_int_phys_base)
213 +{
214 +       return 0;
215 +}
216 +#endif
217 +
218  #ifdef CONFIG_SMP
219  static int armada_xp_set_affinity(struct irq_data *d,
220                                   const struct cpumask *mask_val, bool force)
221 @@ -214,12 +365,39 @@ armada_370_xp_handle_irq(struct pt_regs
222                 if (irqnr > 1022)
223                         break;
224  
225 -               if (irqnr > 0) {
226 +               if (irqnr > 1) {
227                         irqnr = irq_find_mapping(armada_370_xp_mpic_domain,
228                                         irqnr);
229                         handle_IRQ(irqnr, regs);
230                         continue;
231                 }
232 +
233 +#ifdef CONFIG_PCI_MSI
234 +               /* MSI handling */
235 +               if (irqnr == 1) {
236 +                       u32 msimask, msinr;
237 +
238 +                       msimask = readl_relaxed(per_cpu_int_base +
239 +                                               ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS)
240 +                               & PCI_MSI_DOORBELL_MASK;
241 +
242 +                       writel(~PCI_MSI_DOORBELL_MASK, per_cpu_int_base +
243 +                              ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
244 +
245 +                       for (msinr = PCI_MSI_DOORBELL_START;
246 +                            msinr < PCI_MSI_DOORBELL_END; msinr++) {
247 +                               int irq;
248 +
249 +                               if (!(msimask & BIT(msinr)))
250 +                                       continue;
251 +
252 +                               irq = irq_find_mapping(armada_370_xp_msi_domain,
253 +                                                      msinr - 16);
254 +                               handle_IRQ(irq, regs);
255 +                       }
256 +               }
257 +#endif
258 +
259  #ifdef CONFIG_SMP
260                 /* IPI Handling */
261                 if (irqnr == 0) {
262 @@ -292,6 +470,8 @@ static int __init armada_370_xp_mpic_of_
263  
264  #endif
265  
266 +       armada_370_xp_msi_init(node, main_int_res.start);
267 +
268         set_handle_irq(armada_370_xp_handle_irq);
269  
270         return 0;