aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-vt8500/irq.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-vt8500/irq.c')
-rw-r--r--arch/arm/mach-vt8500/irq.c209
1 files changed, 125 insertions, 84 deletions
diff --git a/arch/arm/mach-vt8500/irq.c b/arch/arm/mach-vt8500/irq.c
index 642de0408f25..f8f9ab9bc56e 100644
--- a/arch/arm/mach-vt8500/irq.c
+++ b/arch/arm/mach-vt8500/irq.c
@@ -1,6 +1,7 @@
1/* 1/*
2 * arch/arm/mach-vt8500/irq.c 2 * arch/arm/mach-vt8500/irq.c
3 * 3 *
4 * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
4 * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> 5 * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
5 * 6 *
6 * This program is free software; you can redistribute it and/or modify 7 * This program is free software; you can redistribute it and/or modify
@@ -18,81 +19,102 @@
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */ 20 */
20 21
22/*
23 * This file is copied and modified from the original irq.c provided by
24 * Alexey Charkov. Minor changes have been made for Device Tree Support.
25 */
26
27#include <linux/slab.h>
21#include <linux/io.h> 28#include <linux/io.h>
22#include <linux/irq.h> 29#include <linux/irq.h>
30#include <linux/irqdomain.h>
23#include <linux/interrupt.h> 31#include <linux/interrupt.h>
32#include <linux/bitops.h>
33
34#include <linux/of.h>
35#include <linux/of_irq.h>
36#include <linux/of_address.h>
24 37
25#include <asm/irq.h> 38#include <asm/irq.h>
26 39
27#include "devices.h"
28 40
29#define VT8500_IC_DCTR 0x40 /* Destination control 41#define VT8500_ICPC_IRQ 0x20
30 register, 64*u8 */ 42#define VT8500_ICPC_FIQ 0x24
31#define VT8500_INT_ENABLE (1 << 3) 43#define VT8500_ICDC 0x40 /* Destination Control 64*u32 */
32#define VT8500_TRIGGER_HIGH (0 << 4) 44#define VT8500_ICIS 0x80 /* Interrupt status, 16*u32 */
33#define VT8500_TRIGGER_RISING (1 << 4) 45
34#define VT8500_TRIGGER_FALLING (2 << 4) 46/* ICPC */
47#define ICPC_MASK 0x3F
48#define ICPC_ROTATE BIT(6)
49
50/* IC_DCTR */
51#define ICDC_IRQ 0x00
52#define ICDC_FIQ 0x01
53#define ICDC_DSS0 0x02
54#define ICDC_DSS1 0x03
55#define ICDC_DSS2 0x04
56#define ICDC_DSS3 0x05
57#define ICDC_DSS4 0x06
58#define ICDC_DSS5 0x07
59
60#define VT8500_INT_DISABLE 0
61#define VT8500_INT_ENABLE BIT(3)
62
63#define VT8500_TRIGGER_HIGH 0
64#define VT8500_TRIGGER_RISING BIT(5)
65#define VT8500_TRIGGER_FALLING BIT(6)
35#define VT8500_EDGE ( VT8500_TRIGGER_RISING \ 66#define VT8500_EDGE ( VT8500_TRIGGER_RISING \
36 | VT8500_TRIGGER_FALLING) 67 | VT8500_TRIGGER_FALLING)
37#define VT8500_IC_STATUS 0x80 /* Interrupt status, 2*u32 */
38 68
39static void __iomem *ic_regbase; 69static int irq_cnt;
40static void __iomem *sic_regbase; 70
71struct vt8500_irq_priv {
72 void __iomem *base;
73};
41 74
42static void vt8500_irq_mask(struct irq_data *d) 75static void vt8500_irq_mask(struct irq_data *d)
43{ 76{
44 void __iomem *base = ic_regbase; 77 struct vt8500_irq_priv *priv =
45 unsigned irq = d->irq; 78 (struct vt8500_irq_priv *)(d->domain->host_data);
79 void __iomem *base = priv->base;
46 u8 edge; 80 u8 edge;
47 81
48 if (irq >= 64) { 82 edge = readb(base + VT8500_ICDC + d->hwirq) & VT8500_EDGE;
49 base = sic_regbase;
50 irq -= 64;
51 }
52 edge = readb(base + VT8500_IC_DCTR + irq) & VT8500_EDGE;
53 if (edge) { 83 if (edge) {
54 void __iomem *stat_reg = base + VT8500_IC_STATUS 84 void __iomem *stat_reg = base + VT8500_ICIS
55 + (irq < 32 ? 0 : 4); 85 + (d->hwirq < 32 ? 0 : 4);
56 unsigned status = readl(stat_reg); 86 unsigned status = readl(stat_reg);
57 87
58 status |= (1 << (irq & 0x1f)); 88 status |= (1 << (d->hwirq & 0x1f));
59 writel(status, stat_reg); 89 writel(status, stat_reg);
60 } else { 90 } else {
61 u8 dctr = readb(base + VT8500_IC_DCTR + irq); 91 u8 dctr = readb(base + VT8500_ICDC + d->hwirq);
62 92
63 dctr &= ~VT8500_INT_ENABLE; 93 dctr &= ~VT8500_INT_ENABLE;
64 writeb(dctr, base + VT8500_IC_DCTR + irq); 94 writeb(dctr, base + VT8500_ICDC + d->hwirq);
65 } 95 }
66} 96}
67 97
68static void vt8500_irq_unmask(struct irq_data *d) 98static void vt8500_irq_unmask(struct irq_data *d)
69{ 99{
70 void __iomem *base = ic_regbase; 100 struct vt8500_irq_priv *priv =
71 unsigned irq = d->irq; 101 (struct vt8500_irq_priv *)(d->domain->host_data);
102 void __iomem *base = priv->base;
72 u8 dctr; 103 u8 dctr;
73 104
74 if (irq >= 64) { 105 dctr = readb(base + VT8500_ICDC + d->hwirq);
75 base = sic_regbase;
76 irq -= 64;
77 }
78 dctr = readb(base + VT8500_IC_DCTR + irq);
79 dctr |= VT8500_INT_ENABLE; 106 dctr |= VT8500_INT_ENABLE;
80 writeb(dctr, base + VT8500_IC_DCTR + irq); 107 writeb(dctr, base + VT8500_ICDC + d->hwirq);
81} 108}
82 109
83static int vt8500_irq_set_type(struct irq_data *d, unsigned int flow_type) 110static int vt8500_irq_set_type(struct irq_data *d, unsigned int flow_type)
84{ 111{
85 void __iomem *base = ic_regbase; 112 struct vt8500_irq_priv *priv =
86 unsigned irq = d->irq; 113 (struct vt8500_irq_priv *)(d->domain->host_data);
87 unsigned orig_irq = irq; 114 void __iomem *base = priv->base;
88 u8 dctr; 115 u8 dctr;
89 116
90 if (irq >= 64) { 117 dctr = readb(base + VT8500_ICDC + d->hwirq);
91 base = sic_regbase;
92 irq -= 64;
93 }
94
95 dctr = readb(base + VT8500_IC_DCTR + irq);
96 dctr &= ~VT8500_EDGE; 118 dctr &= ~VT8500_EDGE;
97 119
98 switch (flow_type) { 120 switch (flow_type) {
@@ -100,18 +122,18 @@ static int vt8500_irq_set_type(struct irq_data *d, unsigned int flow_type)
100 return -EINVAL; 122 return -EINVAL;
101 case IRQF_TRIGGER_HIGH: 123 case IRQF_TRIGGER_HIGH:
102 dctr |= VT8500_TRIGGER_HIGH; 124 dctr |= VT8500_TRIGGER_HIGH;
103 __irq_set_handler_locked(orig_irq, handle_level_irq); 125 __irq_set_handler_locked(d->irq, handle_level_irq);
104 break; 126 break;
105 case IRQF_TRIGGER_FALLING: 127 case IRQF_TRIGGER_FALLING:
106 dctr |= VT8500_TRIGGER_FALLING; 128 dctr |= VT8500_TRIGGER_FALLING;
107 __irq_set_handler_locked(orig_irq, handle_edge_irq); 129 __irq_set_handler_locked(d->irq, handle_edge_irq);
108 break; 130 break;
109 case IRQF_TRIGGER_RISING: 131 case IRQF_TRIGGER_RISING:
110 dctr |= VT8500_TRIGGER_RISING; 132 dctr |= VT8500_TRIGGER_RISING;
111 __irq_set_handler_locked(orig_irq, handle_edge_irq); 133 __irq_set_handler_locked(d->irq, handle_edge_irq);
112 break; 134 break;
113 } 135 }
114 writeb(dctr, base + VT8500_IC_DCTR + irq); 136 writeb(dctr, base + VT8500_ICDC + d->hwirq);
115 137
116 return 0; 138 return 0;
117} 139}
@@ -124,57 +146,76 @@ static struct irq_chip vt8500_irq_chip = {
124 .irq_set_type = vt8500_irq_set_type, 146 .irq_set_type = vt8500_irq_set_type,
125}; 147};
126 148
127void __init vt8500_init_irq(void) 149static void __init vt8500_init_irq_hw(void __iomem *base)
128{ 150{
129 unsigned int i; 151 unsigned int i;
130 152
131 ic_regbase = ioremap(wmt_ic_base, SZ_64K); 153 /* Enable rotating priority for IRQ */
154 writel(ICPC_ROTATE, base + VT8500_ICPC_IRQ);
155 writel(0x00, base + VT8500_ICPC_FIQ);
132 156
133 if (ic_regbase) { 157 for (i = 0; i < 64; i++) {
134 /* Enable rotating priority for IRQ */ 158 /* Disable all interrupts and route them to IRQ */
135 writel((1 << 6), ic_regbase + 0x20); 159 writeb(VT8500_INT_DISABLE | ICDC_IRQ,
136 writel(0, ic_regbase + 0x24); 160 base + VT8500_ICDC + i);
161 }
162}
137 163
138 for (i = 0; i < wmt_nr_irqs; i++) { 164static int vt8500_irq_map(struct irq_domain *h, unsigned int virq,
139 /* Disable all interrupts and route them to IRQ */ 165 irq_hw_number_t hw)
140 writeb(0x00, ic_regbase + VT8500_IC_DCTR + i); 166{
167 irq_set_chip_and_handler(virq, &vt8500_irq_chip, handle_level_irq);
168 set_irq_flags(virq, IRQF_VALID);
141 169
142 irq_set_chip_and_handler(i, &vt8500_irq_chip, 170 return 0;
143 handle_level_irq);
144 set_irq_flags(i, IRQF_VALID);
145 }
146 } else {
147 printk(KERN_ERR "Unable to remap the Interrupt Controller registers, not enabling IRQs!\n");
148 }
149} 171}
150 172
151void __init wm8505_init_irq(void) 173static struct irq_domain_ops vt8500_irq_domain_ops = {
174 .map = vt8500_irq_map,
175 .xlate = irq_domain_xlate_onecell,
176};
177
178int __init vt8500_irq_init(struct device_node *node, struct device_node *parent)
152{ 179{
153 unsigned int i; 180 struct irq_domain *vt8500_irq_domain;
181 struct vt8500_irq_priv *priv;
182 int irq, i;
183 struct device_node *np = node;
184
185 priv = kzalloc(sizeof(struct vt8500_irq_priv), GFP_KERNEL);
186 priv->base = of_iomap(np, 0);
187
188 vt8500_irq_domain = irq_domain_add_legacy(node, 64, irq_cnt, 0,
189 &vt8500_irq_domain_ops, priv);
190 if (!vt8500_irq_domain)
191 pr_err("%s: Unable to add wmt irq domain!\n", __func__);
192
193 irq_set_default_host(vt8500_irq_domain);
194
195 vt8500_init_irq_hw(priv->base);
154 196
155 ic_regbase = ioremap(wmt_ic_base, SZ_64K); 197 pr_info("Added IRQ Controller @ %x [virq_base = %d]\n",
156 sic_regbase = ioremap(wmt_sic_base, SZ_64K); 198 (u32)(priv->base), irq_cnt);
157 199
158 if (ic_regbase && sic_regbase) { 200 /* check if this is a slaved controller */
159 /* Enable rotating priority for IRQ */ 201 if (of_irq_count(np) != 0) {
160 writel((1 << 6), ic_regbase + 0x20); 202 /* check that we have the correct number of interrupts */
161 writel(0, ic_regbase + 0x24); 203 if (of_irq_count(np) != 8) {
162 writel((1 << 6), sic_regbase + 0x20); 204 pr_err("%s: Incorrect IRQ map for slave controller\n",
163 writel(0, sic_regbase + 0x24); 205 __func__);
164 206 return -EINVAL;
165 for (i = 0; i < wmt_nr_irqs; i++) {
166 /* Disable all interrupts and route them to IRQ */
167 if (i < 64)
168 writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
169 else
170 writeb(0x00, sic_regbase + VT8500_IC_DCTR
171 + i - 64);
172
173 irq_set_chip_and_handler(i, &vt8500_irq_chip,
174 handle_level_irq);
175 set_irq_flags(i, IRQF_VALID);
176 } 207 }
177 } else { 208
178 printk(KERN_ERR "Unable to remap the Interrupt Controller registers, not enabling IRQs!\n"); 209 for (i = 0; i < 8; i++) {
210 irq = irq_of_parse_and_map(np, i);
211 enable_irq(irq);
212 }
213
214 pr_info("vt8500-irq: Enabled slave->parent interrupts\n");
179 } 215 }
216
217 irq_cnt += 64;
218
219 return 0;
180} 220}
221