Merge remote-tracking branch 'asoc/fix/samsung' into asoc-linus
[deliverable/linux.git] / arch / mips / bcm3384 / irq.c
CommitLineData
d666cd02
KC
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
34struct bcm3384_intc {
35 int n_words;
36 void __iomem *reg[MAX_WORDS];
37 u32 enable[MAX_WORDS];
38 spinlock_t lock;
39};
40
41static 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
54static 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
67static 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
74unsigned int get_c0_compare_int(void)
75{
76 return CP0_LEGACY_COMPARE_IRQ;
77}
78
79static 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
102asmlinkage 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
112static 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
118static const struct irq_domain_ops irq_domain_ops = {
119 .xlate = irq_domain_xlate_onecell,
120 .map = intc_map,
121};
122
123static 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
147static 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
181static struct of_device_id of_irq_ids[] __initdata = {
182 { .compatible = "mti,cpu-interrupt-controller",
0e16d1e3 183 .data = mips_cpu_irq_of_init },
d666cd02
KC
184 { .compatible = "brcm,bcm3384-intc",
185 .data = intc_of_init },
186 {},
187};
188
189void __init arch_init_irq(void)
190{
191 bmips_tp1_irqs = 0;
192 of_irq_init(of_irq_ids);
193}
This page took 0.041874 seconds and 5 git commands to generate.