aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/sh/intc/chip.c
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2010-10-05 09:10:30 -0400
committerPaul Mundt <lethal@linux-sh.org>2010-10-05 09:10:30 -0400
commit2be6bb0c79c7fbda3425b65ee51c558bbaf4cf91 (patch)
treedb0dafd7e7f83945edc2c50c358a3d81fca960c3 /drivers/sh/intc/chip.c
parentd74310d3b18aabbb7d0549ea9e3fd3259c1dce00 (diff)
sh: intc: Split up the INTC code.
This splits up the sh intc core in to something more vaguely resembling a subsystem. Most of the functionality was alread fairly well compartmentalized, and there were only a handful of interdependencies that needed to be resolved in the process. This also serves as future-proofing for the genirq and sparseirq rework, which will make some of the split out functionality wholly generic, allowing things to be killed off in place with minimal migration pain. Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'drivers/sh/intc/chip.c')
-rw-r--r--drivers/sh/intc/chip.c215
1 files changed, 215 insertions, 0 deletions
diff --git a/drivers/sh/intc/chip.c b/drivers/sh/intc/chip.c
new file mode 100644
index 00000000000..35c03706cc2
--- /dev/null
+++ b/drivers/sh/intc/chip.c
@@ -0,0 +1,215 @@
1/*
2 * IRQ chip definitions for INTC IRQs.
3 *
4 * Copyright (C) 2007, 2008 Magnus Damm
5 * Copyright (C) 2009, 2010 Paul Mundt
6 *
7 * This file is subject to the terms and conditions of the GNU General Public
8 * License. See the file "COPYING" in the main directory of this archive
9 * for more details.
10 */
11#include <linux/cpumask.h>
12#include <linux/io.h>
13#include "internals.h"
14
15void _intc_enable(unsigned int irq, unsigned long handle)
16{
17 struct intc_desc_int *d = get_intc_desc(irq);
18 unsigned long addr;
19 unsigned int cpu;
20
21 for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) {
22#ifdef CONFIG_SMP
23 if (!cpumask_test_cpu(cpu, irq_to_desc(irq)->affinity))
24 continue;
25#endif
26 addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu);
27 intc_enable_fns[_INTC_MODE(handle)](addr, handle, intc_reg_fns\
28 [_INTC_FN(handle)], irq);
29 }
30
31 intc_balancing_enable(irq);
32}
33
34static void intc_enable(unsigned int irq)
35{
36 _intc_enable(irq, (unsigned long)get_irq_chip_data(irq));
37}
38
39static void intc_disable(unsigned int irq)
40{
41 struct intc_desc_int *d = get_intc_desc(irq);
42 unsigned long handle = (unsigned long)get_irq_chip_data(irq);
43 unsigned long addr;
44 unsigned int cpu;
45
46 intc_balancing_disable(irq);
47
48 for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) {
49#ifdef CONFIG_SMP
50 if (!cpumask_test_cpu(cpu, irq_to_desc(irq)->affinity))
51 continue;
52#endif
53 addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu);
54 intc_disable_fns[_INTC_MODE(handle)](addr, handle,intc_reg_fns\
55 [_INTC_FN(handle)], irq);
56 }
57}
58
59static int intc_set_wake(unsigned int irq, unsigned int on)
60{
61 return 0; /* allow wakeup, but setup hardware in intc_suspend() */
62}
63
64#ifdef CONFIG_SMP
65/*
66 * This is held with the irq desc lock held, so we don't require any
67 * additional locking here at the intc desc level. The affinity mask is
68 * later tested in the enable/disable paths.
69 */
70static int intc_set_affinity(unsigned int irq, const struct cpumask *cpumask)
71{
72 if (!cpumask_intersects(cpumask, cpu_online_mask))
73 return -1;
74
75 cpumask_copy(irq_to_desc(irq)->affinity, cpumask);
76
77 return 0;
78}
79#endif
80
81static void intc_mask_ack(unsigned int irq)
82{
83 struct intc_desc_int *d = get_intc_desc(irq);
84 unsigned long handle = intc_get_ack_handle(irq);
85 unsigned long addr;
86
87 intc_disable(irq);
88
89 /* read register and write zero only to the associated bit */
90 if (handle) {
91 unsigned int value;
92
93 addr = INTC_REG(d, _INTC_ADDR_D(handle), 0);
94 value = intc_set_field_from_handle(0, 1, handle);
95
96 switch (_INTC_FN(handle)) {
97 case REG_FN_MODIFY_BASE + 0: /* 8bit */
98 __raw_readb(addr);
99 __raw_writeb(0xff ^ value, addr);
100 break;
101 case REG_FN_MODIFY_BASE + 1: /* 16bit */
102 __raw_readw(addr);
103 __raw_writew(0xffff ^ value, addr);
104 break;
105 case REG_FN_MODIFY_BASE + 3: /* 32bit */
106 __raw_readl(addr);
107 __raw_writel(0xffffffff ^ value, addr);
108 break;
109 default:
110 BUG();
111 break;
112 }
113 }
114}
115
116static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp,
117 unsigned int nr_hp,
118 unsigned int irq)
119{
120 int i;
121
122 /*
123 * this doesn't scale well, but...
124 *
125 * this function should only be used for cerain uncommon
126 * operations such as intc_set_priority() and intc_set_type()
127 * and in those rare cases performance doesn't matter that much.
128 * keeping the memory footprint low is more important.
129 *
130 * one rather simple way to speed this up and still keep the
131 * memory footprint down is to make sure the array is sorted
132 * and then perform a bisect to lookup the irq.
133 */
134 for (i = 0; i < nr_hp; i++) {
135 if ((hp + i)->irq != irq)
136 continue;
137
138 return hp + i;
139 }
140
141 return NULL;
142}
143
144int intc_set_priority(unsigned int irq, unsigned int prio)
145{
146 struct intc_desc_int *d = get_intc_desc(irq);
147 struct intc_handle_int *ihp;
148
149 if (!intc_get_prio_level(irq) || prio <= 1)
150 return -EINVAL;
151
152 ihp = intc_find_irq(d->prio, d->nr_prio, irq);
153 if (ihp) {
154 if (prio >= (1 << _INTC_WIDTH(ihp->handle)))
155 return -EINVAL;
156
157 intc_set_prio_level(irq, prio);
158
159 /*
160 * only set secondary masking method directly
161 * primary masking method is using intc_prio_level[irq]
162 * priority level will be set during next enable()
163 */
164 if (_INTC_FN(ihp->handle) != REG_FN_ERR)
165 _intc_enable(irq, ihp->handle);
166 }
167 return 0;
168}
169
170#define VALID(x) (x | 0x80)
171
172static unsigned char intc_irq_sense_table[IRQ_TYPE_SENSE_MASK + 1] = {
173 [IRQ_TYPE_EDGE_FALLING] = VALID(0),
174 [IRQ_TYPE_EDGE_RISING] = VALID(1),
175 [IRQ_TYPE_LEVEL_LOW] = VALID(2),
176 /* SH7706, SH7707 and SH7709 do not support high level triggered */
177#if !defined(CONFIG_CPU_SUBTYPE_SH7706) && \
178 !defined(CONFIG_CPU_SUBTYPE_SH7707) && \
179 !defined(CONFIG_CPU_SUBTYPE_SH7709)
180 [IRQ_TYPE_LEVEL_HIGH] = VALID(3),
181#endif
182};
183
184static int intc_set_type(unsigned int irq, unsigned int type)
185{
186 struct intc_desc_int *d = get_intc_desc(irq);
187 unsigned char value = intc_irq_sense_table[type & IRQ_TYPE_SENSE_MASK];
188 struct intc_handle_int *ihp;
189 unsigned long addr;
190
191 if (!value)
192 return -EINVAL;
193
194 ihp = intc_find_irq(d->sense, d->nr_sense, irq);
195 if (ihp) {
196 addr = INTC_REG(d, _INTC_ADDR_E(ihp->handle), 0);
197 intc_reg_fns[_INTC_FN(ihp->handle)](addr, ihp->handle, value);
198 }
199
200 return 0;
201}
202
203struct irq_chip intc_irq_chip = {
204 .mask = intc_disable,
205 .unmask = intc_enable,
206 .mask_ack = intc_mask_ack,
207 .enable = intc_enable,
208 .disable = intc_disable,
209 .shutdown = intc_disable,
210 .set_type = intc_set_type,
211 .set_wake = intc_set_wake,
212#ifdef CONFIG_SMP
213 .set_affinity = intc_set_affinity,
214#endif
215};