Merge tag 'iwlwifi-next-for-kalle-2014-12-30' of https://git.kernel.org/pub/scm/linux...
[cascardo/linux.git] / arch / mips / bcm3384 / irq.c
1 /*
2  * This program is free software; you can redistribute it and/or modify it
3  * under the terms of the GNU General Public License version 2 as published
4  * by the Free Software Foundation.
5  *
6  * Partially based on arch/mips/ralink/irq.c
7  *
8  * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org>
9  * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
10  * Copyright (C) 2014 Kevin Cernekee <cernekee@gmail.com>
11  */
12
13 #include <linux/io.h>
14 #include <linux/bitops.h>
15 #include <linux/of_platform.h>
16 #include <linux/of_address.h>
17 #include <linux/of_irq.h>
18 #include <linux/irqdomain.h>
19 #include <linux/interrupt.h>
20 #include <linux/slab.h>
21 #include <linux/spinlock.h>
22
23 #include <asm/bmips.h>
24 #include <asm/irq_cpu.h>
25 #include <asm/mipsregs.h>
26
27 /* INTC register offsets */
28 #define INTC_REG_ENABLE         0x00
29 #define INTC_REG_STATUS         0x04
30
31 #define MAX_WORDS               2
32 #define IRQS_PER_WORD           32
33
34 struct bcm3384_intc {
35         int                     n_words;
36         void __iomem            *reg[MAX_WORDS];
37         u32                     enable[MAX_WORDS];
38         spinlock_t              lock;
39 };
40
41 static void bcm3384_intc_irq_unmask(struct irq_data *d)
42 {
43         struct bcm3384_intc *priv = d->domain->host_data;
44         unsigned long flags;
45         int idx = d->hwirq / IRQS_PER_WORD;
46         int bit = d->hwirq % IRQS_PER_WORD;
47
48         spin_lock_irqsave(&priv->lock, flags);
49         priv->enable[idx] |= BIT(bit);
50         __raw_writel(priv->enable[idx], priv->reg[idx] + INTC_REG_ENABLE);
51         spin_unlock_irqrestore(&priv->lock, flags);
52 }
53
54 static void bcm3384_intc_irq_mask(struct irq_data *d)
55 {
56         struct bcm3384_intc *priv = d->domain->host_data;
57         unsigned long flags;
58         int idx = d->hwirq / IRQS_PER_WORD;
59         int bit = d->hwirq % IRQS_PER_WORD;
60
61         spin_lock_irqsave(&priv->lock, flags);
62         priv->enable[idx] &= ~BIT(bit);
63         __raw_writel(priv->enable[idx], priv->reg[idx] + INTC_REG_ENABLE);
64         spin_unlock_irqrestore(&priv->lock, flags);
65 }
66
67 static struct irq_chip bcm3384_intc_irq_chip = {
68         .name           = "INTC",
69         .irq_unmask     = bcm3384_intc_irq_unmask,
70         .irq_mask       = bcm3384_intc_irq_mask,
71         .irq_mask_ack   = bcm3384_intc_irq_mask,
72 };
73
74 unsigned int get_c0_compare_int(void)
75 {
76         return CP0_LEGACY_COMPARE_IRQ;
77 }
78
79 static void bcm3384_intc_irq_handler(unsigned int irq, struct irq_desc *desc)
80 {
81         struct irq_domain *domain = irq_get_handler_data(irq);
82         struct bcm3384_intc *priv = domain->host_data;
83         unsigned long flags;
84         unsigned int idx;
85
86         for (idx = 0; idx < priv->n_words; idx++) {
87                 unsigned long pending;
88                 int hwirq;
89
90                 spin_lock_irqsave(&priv->lock, flags);
91                 pending = __raw_readl(priv->reg[idx] + INTC_REG_STATUS) &
92                           priv->enable[idx];
93                 spin_unlock_irqrestore(&priv->lock, flags);
94
95                 for_each_set_bit(hwirq, &pending, IRQS_PER_WORD) {
96                         generic_handle_irq(irq_find_mapping(domain,
97                                            hwirq + idx * IRQS_PER_WORD));
98                 }
99         }
100 }
101
102 asmlinkage void plat_irq_dispatch(void)
103 {
104         unsigned long pending =
105                 (read_c0_status() & read_c0_cause() & ST0_IM) >> STATUSB_IP0;
106         int bit;
107
108         for_each_set_bit(bit, &pending, 8)
109                 do_IRQ(MIPS_CPU_IRQ_BASE + bit);
110 }
111
112 static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
113 {
114         irq_set_chip_and_handler(irq, &bcm3384_intc_irq_chip, handle_level_irq);
115         return 0;
116 }
117
118 static const struct irq_domain_ops irq_domain_ops = {
119         .xlate = irq_domain_xlate_onecell,
120         .map = intc_map,
121 };
122
123 static int __init ioremap_one_pair(struct bcm3384_intc *priv,
124                                    struct device_node *node,
125                                    int idx)
126 {
127         struct resource res;
128
129         if (of_address_to_resource(node, idx, &res))
130                 return 0;
131
132         if (request_mem_region(res.start, resource_size(&res),
133                                res.name) < 0)
134                 pr_err("Failed to request INTC register region\n");
135
136         priv->reg[idx] = ioremap_nocache(res.start, resource_size(&res));
137         if (!priv->reg[idx])
138                 panic("Failed to ioremap INTC register range");
139
140         /* start up with everything masked before we hook the parent IRQ */
141         __raw_writel(0, priv->reg[idx] + INTC_REG_ENABLE);
142         priv->enable[idx] = 0;
143
144         return IRQS_PER_WORD;
145 }
146
147 static int __init intc_of_init(struct device_node *node,
148                                struct device_node *parent)
149 {
150         struct irq_domain *domain;
151         unsigned int parent_irq, n_irqs = 0;
152         struct bcm3384_intc *priv;
153
154         priv = kzalloc(sizeof(*priv), GFP_KERNEL);
155         if (!priv)
156                 panic("Failed to allocate bcm3384_intc struct");
157
158         spin_lock_init(&priv->lock);
159
160         parent_irq = irq_of_parse_and_map(node, 0);
161         if (!parent_irq)
162                 panic("Failed to get INTC IRQ");
163
164         n_irqs += ioremap_one_pair(priv, node, 0);
165         n_irqs += ioremap_one_pair(priv, node, 1);
166
167         if (!n_irqs)
168                 panic("Failed to map INTC registers");
169
170         priv->n_words = n_irqs / IRQS_PER_WORD;
171         domain = irq_domain_add_linear(node, n_irqs, &irq_domain_ops, priv);
172         if (!domain)
173                 panic("Failed to add irqdomain");
174
175         irq_set_chained_handler(parent_irq, bcm3384_intc_irq_handler);
176         irq_set_handler_data(parent_irq, domain);
177
178         return 0;
179 }
180
181 static struct of_device_id of_irq_ids[] __initdata = {
182         { .compatible = "mti,cpu-interrupt-controller",
183           .data = mips_cpu_intc_init },
184         { .compatible = "brcm,bcm3384-intc",
185           .data = intc_of_init },
186         {},
187 };
188
189 void __init arch_init_irq(void)
190 {
191         bmips_tp1_irqs = 0;
192         of_irq_init(of_irq_ids);
193 }