Commit | Line | Data |
---|---|---|
0dcdbe6a AN |
1 | /* |
2 | * TX4939 irq routines | |
3 | * Based on linux/arch/mips/kernel/irq_txx9.c, | |
4 | * and RBTX49xx patch from CELF patch archive. | |
5 | * | |
6 | * Copyright 2001, 2003-2005 MontaVista Software Inc. | |
7 | * Author: MontaVista Software, Inc. | |
70342287 RB |
8 | * ahennessy@mvista.com |
9 | * source@mvista.com | |
0dcdbe6a AN |
10 | * Copyright (C) 2000-2001,2005-2007 Toshiba Corporation |
11 | * | |
12 | * This file is subject to the terms and conditions of the GNU General Public | |
13 | * License. See the file "COPYING" in the main directory of this archive | |
14 | * for more details. | |
15 | */ | |
16 | /* | |
17 | * TX4939 defines 64 IRQs. | |
18 | * Similer to irq_txx9.c but different register layouts. | |
19 | */ | |
20 | #include <linux/init.h> | |
21 | #include <linux/interrupt.h> | |
ca4d3e67 | 22 | #include <linux/irq.h> |
0dcdbe6a AN |
23 | #include <linux/types.h> |
24 | #include <asm/irq_cpu.h> | |
25 | #include <asm/txx9irq.h> | |
26 | #include <asm/txx9/tx4939.h> | |
27 | ||
28 | /* IRCER : Int. Control Enable */ | |
29 | #define TXx9_IRCER_ICE 0x00000001 | |
30 | ||
31 | /* IRCR : Int. Control */ | |
32 | #define TXx9_IRCR_LOW 0x00000000 | |
33 | #define TXx9_IRCR_HIGH 0x00000001 | |
34 | #define TXx9_IRCR_DOWN 0x00000002 | |
35 | #define TXx9_IRCR_UP 0x00000003 | |
36 | #define TXx9_IRCR_EDGE(cr) ((cr) & 0x00000002) | |
37 | ||
38 | /* IRSCR : Int. Status Control */ | |
39 | #define TXx9_IRSCR_EIClrE 0x00000100 | |
40 | #define TXx9_IRSCR_EIClr_MASK 0x0000000f | |
41 | ||
42 | /* IRCSR : Int. Current Status */ | |
43 | #define TXx9_IRCSR_IF 0x00010000 | |
44 | ||
45 | #define irc_dlevel 0 | |
46 | #define irc_elevel 1 | |
47 | ||
48 | static struct { | |
49 | unsigned char level; | |
50 | unsigned char mode; | |
51 | } tx4939irq[TX4939_NUM_IR] __read_mostly; | |
52 | ||
d7ae7c71 | 53 | static void tx4939_irq_unmask(struct irq_data *d) |
0dcdbe6a | 54 | { |
d7ae7c71 | 55 | unsigned int irq_nr = d->irq - TXX9_IRQ_BASE; |
0dcdbe6a AN |
56 | u32 __iomem *lvlp; |
57 | int ofs; | |
58 | if (irq_nr < 32) { | |
59 | irq_nr--; | |
60 | lvlp = &tx4939_ircptr->lvl[(irq_nr % 16) / 2].r; | |
61 | } else { | |
62 | irq_nr -= 32; | |
63 | lvlp = &tx4939_ircptr->lvl[8 + (irq_nr % 16) / 2].r; | |
64 | } | |
65 | ofs = (irq_nr & 16) + (irq_nr & 1) * 8; | |
66 | __raw_writel((__raw_readl(lvlp) & ~(0xff << ofs)) | |
67 | | (tx4939irq[irq_nr].level << ofs), | |
68 | lvlp); | |
69 | } | |
70 | ||
d7ae7c71 | 71 | static inline void tx4939_irq_mask(struct irq_data *d) |
0dcdbe6a | 72 | { |
d7ae7c71 | 73 | unsigned int irq_nr = d->irq - TXX9_IRQ_BASE; |
0dcdbe6a AN |
74 | u32 __iomem *lvlp; |
75 | int ofs; | |
76 | if (irq_nr < 32) { | |
77 | irq_nr--; | |
78 | lvlp = &tx4939_ircptr->lvl[(irq_nr % 16) / 2].r; | |
79 | } else { | |
80 | irq_nr -= 32; | |
81 | lvlp = &tx4939_ircptr->lvl[8 + (irq_nr % 16) / 2].r; | |
82 | } | |
83 | ofs = (irq_nr & 16) + (irq_nr & 1) * 8; | |
84 | __raw_writel((__raw_readl(lvlp) & ~(0xff << ofs)) | |
85 | | (irc_dlevel << ofs), | |
86 | lvlp); | |
87 | mmiowb(); | |
88 | } | |
89 | ||
d7ae7c71 | 90 | static void tx4939_irq_mask_ack(struct irq_data *d) |
0dcdbe6a | 91 | { |
d7ae7c71 | 92 | unsigned int irq_nr = d->irq - TXX9_IRQ_BASE; |
0dcdbe6a | 93 | |
d7ae7c71 | 94 | tx4939_irq_mask(d); |
0dcdbe6a AN |
95 | if (TXx9_IRCR_EDGE(tx4939irq[irq_nr].mode)) { |
96 | irq_nr--; | |
97 | /* clear edge detection */ | |
98 | __raw_writel((TXx9_IRSCR_EIClrE | (irq_nr & 0xf)) | |
99 | << (irq_nr & 0x10), | |
100 | &tx4939_ircptr->edc.r); | |
101 | } | |
102 | } | |
103 | ||
d7ae7c71 | 104 | static int tx4939_irq_set_type(struct irq_data *d, unsigned int flow_type) |
0dcdbe6a | 105 | { |
d7ae7c71 | 106 | unsigned int irq_nr = d->irq - TXX9_IRQ_BASE; |
0dcdbe6a AN |
107 | u32 cr; |
108 | u32 __iomem *crp; | |
109 | int ofs; | |
110 | int mode; | |
111 | ||
112 | if (flow_type & IRQF_TRIGGER_PROBE) | |
113 | return 0; | |
114 | switch (flow_type & IRQF_TRIGGER_MASK) { | |
115 | case IRQF_TRIGGER_RISING: | |
116 | mode = TXx9_IRCR_UP; | |
117 | break; | |
118 | case IRQF_TRIGGER_FALLING: | |
119 | mode = TXx9_IRCR_DOWN; | |
120 | break; | |
121 | case IRQF_TRIGGER_HIGH: | |
122 | mode = TXx9_IRCR_HIGH; | |
123 | break; | |
124 | case IRQF_TRIGGER_LOW: | |
125 | mode = TXx9_IRCR_LOW; | |
126 | break; | |
127 | default: | |
128 | return -EINVAL; | |
129 | } | |
130 | if (irq_nr < 32) { | |
131 | irq_nr--; | |
132 | crp = &tx4939_ircptr->dm[(irq_nr & 8) >> 3].r; | |
133 | } else { | |
134 | irq_nr -= 32; | |
135 | crp = &tx4939_ircptr->dm2[((irq_nr & 8) >> 3)].r; | |
136 | } | |
137 | ofs = (((irq_nr & 16) >> 1) | (irq_nr & (8 - 1))) * 2; | |
138 | cr = __raw_readl(crp); | |
139 | cr &= ~(0x3 << ofs); | |
140 | cr |= (mode & 0x3) << ofs; | |
141 | __raw_writel(cr, crp); | |
142 | tx4939irq[irq_nr].mode = mode; | |
143 | return 0; | |
144 | } | |
145 | ||
146 | static struct irq_chip tx4939_irq_chip = { | |
147 | .name = "TX4939", | |
d7ae7c71 TG |
148 | .irq_ack = tx4939_irq_mask_ack, |
149 | .irq_mask = tx4939_irq_mask, | |
150 | .irq_mask_ack = tx4939_irq_mask_ack, | |
151 | .irq_unmask = tx4939_irq_unmask, | |
152 | .irq_set_type = tx4939_irq_set_type, | |
0dcdbe6a AN |
153 | }; |
154 | ||
155 | static int tx4939_irq_set_pri(int irc_irq, int new_pri) | |
156 | { | |
157 | int old_pri; | |
158 | ||
159 | if ((unsigned int)irc_irq >= TX4939_NUM_IR) | |
160 | return 0; | |
161 | old_pri = tx4939irq[irc_irq].level; | |
162 | tx4939irq[irc_irq].level = new_pri; | |
163 | return old_pri; | |
164 | } | |
165 | ||
166 | void __init tx4939_irq_init(void) | |
167 | { | |
168 | int i; | |
169 | ||
170 | mips_cpu_irq_init(); | |
171 | /* disable interrupt control */ | |
172 | __raw_writel(0, &tx4939_ircptr->den.r); | |
173 | __raw_writel(0, &tx4939_ircptr->maskint.r); | |
174 | __raw_writel(0, &tx4939_ircptr->maskext.r); | |
175 | /* irq_base + 0 is not used */ | |
176 | for (i = 1; i < TX4939_NUM_IR; i++) { | |
177 | tx4939irq[i].level = 4; /* middle level */ | |
178 | tx4939irq[i].mode = TXx9_IRCR_LOW; | |
e4ec7989 TG |
179 | irq_set_chip_and_handler(TXX9_IRQ_BASE + i, &tx4939_irq_chip, |
180 | handle_level_irq); | |
0dcdbe6a AN |
181 | } |
182 | ||
183 | /* mask all IRC interrupts */ | |
184 | __raw_writel(0, &tx4939_ircptr->msk.r); | |
185 | for (i = 0; i < 16; i++) | |
186 | __raw_writel(0, &tx4939_ircptr->lvl[i].r); | |
187 | /* setup IRC interrupt mode (Low Active) */ | |
188 | for (i = 0; i < 2; i++) | |
189 | __raw_writel(0, &tx4939_ircptr->dm[i].r); | |
190 | for (i = 0; i < 2; i++) | |
191 | __raw_writel(0, &tx4939_ircptr->dm2[i].r); | |
192 | /* enable interrupt control */ | |
193 | __raw_writel(TXx9_IRCER_ICE, &tx4939_ircptr->den.r); | |
194 | __raw_writel(irc_elevel, &tx4939_ircptr->msk.r); | |
195 | ||
e4ec7989 | 196 | irq_set_chained_handler(MIPS_CPU_IRQ_BASE + TX4939_IRC_INT, |
0dcdbe6a AN |
197 | handle_simple_irq); |
198 | ||
199 | /* raise priority for errors, timers, sio */ | |
200 | tx4939_irq_set_pri(TX4939_IR_WTOERR, 7); | |
201 | tx4939_irq_set_pri(TX4939_IR_PCIERR, 7); | |
202 | tx4939_irq_set_pri(TX4939_IR_PCIPME, 7); | |
203 | for (i = 0; i < TX4939_NUM_IR_TMR; i++) | |
204 | tx4939_irq_set_pri(TX4939_IR_TMR(i), 6); | |
205 | for (i = 0; i < TX4939_NUM_IR_SIO; i++) | |
206 | tx4939_irq_set_pri(TX4939_IR_SIO(i), 5); | |
207 | } | |
208 | ||
209 | int tx4939_irq(void) | |
210 | { | |
211 | u32 csr = __raw_readl(&tx4939_ircptr->cs.r); | |
212 | ||
213 | if (likely(!(csr & TXx9_IRCSR_IF))) | |
214 | return TXX9_IRQ_BASE + (csr & (TX4939_NUM_IR - 1)); | |
215 | return -1; | |
216 | } |