mac80211: minstrel_ht: fix a crash in rate sorting
[cascardo/linux.git] / drivers / irqchip / irq-clps711x.c
1 /*
2  *  CLPS711X IRQ driver
3  *
4  *  Copyright (C) 2013 Alexander Shiyan <shc_work@mail.ru>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  */
11
12 #include <linux/io.h>
13 #include <linux/irq.h>
14 #include <linux/irqdomain.h>
15 #include <linux/of_address.h>
16 #include <linux/of_irq.h>
17 #include <linux/slab.h>
18
19 #include <asm/exception.h>
20 #include <asm/mach/irq.h>
21
22 #include "irqchip.h"
23
24 #define CLPS711X_INTSR1 (0x0240)
25 #define CLPS711X_INTMR1 (0x0280)
26 #define CLPS711X_BLEOI  (0x0600)
27 #define CLPS711X_MCEOI  (0x0640)
28 #define CLPS711X_TEOI   (0x0680)
29 #define CLPS711X_TC1EOI (0x06c0)
30 #define CLPS711X_TC2EOI (0x0700)
31 #define CLPS711X_RTCEOI (0x0740)
32 #define CLPS711X_UMSEOI (0x0780)
33 #define CLPS711X_COEOI  (0x07c0)
34 #define CLPS711X_INTSR2 (0x1240)
35 #define CLPS711X_INTMR2 (0x1280)
36 #define CLPS711X_SRXEOF (0x1600)
37 #define CLPS711X_KBDEOI (0x1700)
38 #define CLPS711X_INTSR3 (0x2240)
39 #define CLPS711X_INTMR3 (0x2280)
40
41 static const struct {
42 #define CLPS711X_FLAG_EN        (1 << 0)
43 #define CLPS711X_FLAG_FIQ       (1 << 1)
44         unsigned int    flags;
45         phys_addr_t     eoi;
46 } clps711x_irqs[] = {
47         [1]     = { CLPS711X_FLAG_FIQ, CLPS711X_BLEOI, },
48         [3]     = { CLPS711X_FLAG_FIQ, CLPS711X_MCEOI, },
49         [4]     = { CLPS711X_FLAG_EN, CLPS711X_COEOI, },
50         [5]     = { CLPS711X_FLAG_EN, },
51         [6]     = { CLPS711X_FLAG_EN, },
52         [7]     = { CLPS711X_FLAG_EN, },
53         [8]     = { CLPS711X_FLAG_EN, CLPS711X_TC1EOI, },
54         [9]     = { CLPS711X_FLAG_EN, CLPS711X_TC2EOI, },
55         [10]    = { CLPS711X_FLAG_EN, CLPS711X_RTCEOI, },
56         [11]    = { CLPS711X_FLAG_EN, CLPS711X_TEOI, },
57         [12]    = { CLPS711X_FLAG_EN, },
58         [13]    = { CLPS711X_FLAG_EN, },
59         [14]    = { CLPS711X_FLAG_EN, CLPS711X_UMSEOI, },
60         [15]    = { CLPS711X_FLAG_EN, CLPS711X_SRXEOF, },
61         [16]    = { CLPS711X_FLAG_EN, CLPS711X_KBDEOI, },
62         [17]    = { CLPS711X_FLAG_EN, },
63         [18]    = { CLPS711X_FLAG_EN, },
64         [28]    = { CLPS711X_FLAG_EN, },
65         [29]    = { CLPS711X_FLAG_EN, },
66         [32]    = { CLPS711X_FLAG_FIQ, },
67 };
68
69 static struct {
70         void __iomem            *base;
71         void __iomem            *intmr[3];
72         void __iomem            *intsr[3];
73         struct irq_domain       *domain;
74         struct irq_domain_ops   ops;
75 } *clps711x_intc;
76
77 static asmlinkage void __exception_irq_entry clps711x_irqh(struct pt_regs *regs)
78 {
79         u32 irqnr, irqstat;
80
81         do {
82                 irqstat = readw_relaxed(clps711x_intc->intmr[0]) &
83                           readw_relaxed(clps711x_intc->intsr[0]);
84                 if (irqstat) {
85                         irqnr = irq_find_mapping(clps711x_intc->domain,
86                                                  fls(irqstat) - 1);
87                         handle_IRQ(irqnr, regs);
88                 }
89
90                 irqstat = readw_relaxed(clps711x_intc->intmr[1]) &
91                           readw_relaxed(clps711x_intc->intsr[1]);
92                 if (irqstat) {
93                         irqnr = irq_find_mapping(clps711x_intc->domain,
94                                                  fls(irqstat) - 1 + 16);
95                         handle_IRQ(irqnr, regs);
96                 }
97         } while (irqstat);
98 }
99
100 static void clps711x_intc_eoi(struct irq_data *d)
101 {
102         irq_hw_number_t hwirq = irqd_to_hwirq(d);
103
104         writel_relaxed(0, clps711x_intc->base + clps711x_irqs[hwirq].eoi);
105 }
106
107 static void clps711x_intc_mask(struct irq_data *d)
108 {
109         irq_hw_number_t hwirq = irqd_to_hwirq(d);
110         void __iomem *intmr = clps711x_intc->intmr[hwirq / 16];
111         u32 tmp;
112
113         tmp = readl_relaxed(intmr);
114         tmp &= ~(1 << (hwirq % 16));
115         writel_relaxed(tmp, intmr);
116 }
117
118 static void clps711x_intc_unmask(struct irq_data *d)
119 {
120         irq_hw_number_t hwirq = irqd_to_hwirq(d);
121         void __iomem *intmr = clps711x_intc->intmr[hwirq / 16];
122         u32 tmp;
123
124         tmp = readl_relaxed(intmr);
125         tmp |= 1 << (hwirq % 16);
126         writel_relaxed(tmp, intmr);
127 }
128
129 static struct irq_chip clps711x_intc_chip = {
130         .name           = "clps711x-intc",
131         .irq_eoi        = clps711x_intc_eoi,
132         .irq_mask       = clps711x_intc_mask,
133         .irq_unmask     = clps711x_intc_unmask,
134 };
135
136 static int __init clps711x_intc_irq_map(struct irq_domain *h, unsigned int virq,
137                                         irq_hw_number_t hw)
138 {
139         irq_flow_handler_t handler = handle_level_irq;
140         unsigned int flags = IRQF_VALID | IRQF_PROBE;
141
142         if (!clps711x_irqs[hw].flags)
143                 return 0;
144
145         if (clps711x_irqs[hw].flags & CLPS711X_FLAG_FIQ) {
146                 handler = handle_bad_irq;
147                 flags |= IRQF_NOAUTOEN;
148         } else if (clps711x_irqs[hw].eoi) {
149                 handler = handle_fasteoi_irq;
150         }
151
152         /* Clear down pending interrupt */
153         if (clps711x_irqs[hw].eoi)
154                 writel_relaxed(0, clps711x_intc->base + clps711x_irqs[hw].eoi);
155
156         irq_set_chip_and_handler(virq, &clps711x_intc_chip, handler);
157         set_irq_flags(virq, flags);
158
159         return 0;
160 }
161
162 static int __init _clps711x_intc_init(struct device_node *np,
163                                       phys_addr_t base, resource_size_t size)
164 {
165         int err;
166
167         clps711x_intc = kzalloc(sizeof(*clps711x_intc), GFP_KERNEL);
168         if (!clps711x_intc)
169                 return -ENOMEM;
170
171         clps711x_intc->base = ioremap(base, size);
172         if (!clps711x_intc->base) {
173                 err = -ENOMEM;
174                 goto out_kfree;
175         }
176
177         clps711x_intc->intsr[0] = clps711x_intc->base + CLPS711X_INTSR1;
178         clps711x_intc->intmr[0] = clps711x_intc->base + CLPS711X_INTMR1;
179         clps711x_intc->intsr[1] = clps711x_intc->base + CLPS711X_INTSR2;
180         clps711x_intc->intmr[1] = clps711x_intc->base + CLPS711X_INTMR2;
181         clps711x_intc->intsr[2] = clps711x_intc->base + CLPS711X_INTSR3;
182         clps711x_intc->intmr[2] = clps711x_intc->base + CLPS711X_INTMR3;
183
184         /* Mask all interrupts */
185         writel_relaxed(0, clps711x_intc->intmr[0]);
186         writel_relaxed(0, clps711x_intc->intmr[1]);
187         writel_relaxed(0, clps711x_intc->intmr[2]);
188
189         err = irq_alloc_descs(-1, 0, ARRAY_SIZE(clps711x_irqs), numa_node_id());
190         if (IS_ERR_VALUE(err))
191                 goto out_iounmap;
192
193         clps711x_intc->ops.map = clps711x_intc_irq_map;
194         clps711x_intc->ops.xlate = irq_domain_xlate_onecell;
195         clps711x_intc->domain =
196                 irq_domain_add_legacy(np, ARRAY_SIZE(clps711x_irqs),
197                                       0, 0, &clps711x_intc->ops, NULL);
198         if (!clps711x_intc->domain) {
199                 err = -ENOMEM;
200                 goto out_irqfree;
201         }
202
203         irq_set_default_host(clps711x_intc->domain);
204         set_handle_irq(clps711x_irqh);
205
206 #ifdef CONFIG_FIQ
207         init_FIQ(0);
208 #endif
209
210         return 0;
211
212 out_irqfree:
213         irq_free_descs(0, ARRAY_SIZE(clps711x_irqs));
214
215 out_iounmap:
216         iounmap(clps711x_intc->base);
217
218 out_kfree:
219         kfree(clps711x_intc);
220
221         return err;
222 }
223
224 void __init clps711x_intc_init(phys_addr_t base, resource_size_t size)
225 {
226         BUG_ON(_clps711x_intc_init(NULL, base, size));
227 }
228
229 #ifdef CONFIG_IRQCHIP
230 static int __init clps711x_intc_init_dt(struct device_node *np,
231                                         struct device_node *parent)
232 {
233         struct resource res;
234         int err;
235
236         err = of_address_to_resource(np, 0, &res);
237         if (err)
238                 return err;
239
240         return _clps711x_intc_init(np, res.start, resource_size(&res));
241 }
242 IRQCHIP_DECLARE(clps711x, "cirrus,clps711x-intc", clps711x_intc_init_dt);
243 #endif