Merge branch 'drm-patches' of master.kernel.org:/pub/scm/linux/kernel/git/airlied...
[linux-drm-fsl-dcu.git] / arch / sh / kernel / cpu / irq / pint.c
1 /*
2  * arch/sh/kernel/cpu/irq/pint.c - Interrupt handling for PINT-based IRQs.
3  *
4  * Copyright (C) 1999  Niibe Yutaka & Takeshi Yaegashi
5  * Copyright (C) 2000  Kazumoto Kojima
6  * Copyright (C) 2003 Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp>
7  *
8  * This file is subject to the terms and conditions of the GNU General Public
9  * License.  See the file "COPYING" in the main directory of this archive
10  * for more details.
11  */
12
13 #include <linux/init.h>
14 #include <linux/irq.h>
15 #include <linux/module.h>
16
17 #include <asm/system.h>
18 #include <asm/io.h>
19 #include <asm/machvec.h>
20
21 #if defined(CONFIG_CPU_SUBTYPE_SH7705)
22 #define INTC_INTER      0xA4000014UL
23 #define INTC_IPRD       0xA4000018UL
24 #define INTC_ICR2       0xA4000012UL
25
26 /* PFC */
27 #define PORT_PACR       0xA4000100UL
28 #define PORT_PBCR       0xA4000102UL
29 #define PORT_PCCR       0xA4000104UL
30 #define PORT_PDCR       0xA4000106UL
31 #define PORT_PECR       0xA4000108UL
32 #define PORT_PFCR       0xA400010AUL
33 #define PORT_PGCR       0xA400010CUL
34 #define PORT_PHCR       0xA400010EUL
35 #define PORT_PJCR       0xA4000110UL
36 #define PORT_PKCR       0xA4000112UL
37 #define PORT_PLCR       0xA4000114UL
38 #define PORT_PMCR       0xA4000118UL
39 #define PORT_PNCR       0xA400011AUL
40 #define PORT_PECR2      0xA4050148UL
41 #define PORT_PFCR2      0xA405014AUL
42 #define PORT_PNCR2      0xA405015AUL
43
44 /* I/O port */
45 #define PORT_PADR       0xA4000120UL
46 #define PORT_PBDR       0xA4000122UL
47 #define PORT_PCDR       0xA4000124UL
48 #define PORT_PDDR       0xA4000126UL
49 #define PORT_PEDR       0xA4000128UL
50 #define PORT_PFDR       0xA400012AUL
51 #define PORT_PGDR       0xA400012CUL
52 #define PORT_PHDR       0xA400012EUL
53 #define PORT_PJDR       0xA4000130UL
54 #define PORT_PKDR       0xA4000132UL
55 #define PORT_PLDR       0xA4000134UL
56 #define PORT_PMDR       0xA4000138UL
57 #define PORT_PNDR       0xA400013AUL
58
59 #define PINT0_IRQ       40
60 #define PINT8_IRQ       41
61 #define PINT_IRQ_BASE   86
62
63 #define PINT0_IPR_ADDR          INTC_IPRD
64 #define PINT0_IPR_POS           3
65 #define PINT0_PRIORITY      2
66
67 #define PINT8_IPR_ADDR          INTC_IPRD
68 #define PINT8_IPR_POS           2
69 #define PINT8_PRIORITY      2
70
71 #endif /* CONFIG_CPU_SUBTYPE_SH7705 */
72
73 static unsigned char pint_map[256];
74 static unsigned long portcr_mask;
75
76 static void enable_pint_irq(unsigned int irq);
77 static void disable_pint_irq(unsigned int irq);
78
79 /* shutdown is same as "disable" */
80 #define shutdown_pint_irq disable_pint_irq
81
82 static void mask_and_ack_pint(unsigned int);
83 static void end_pint_irq(unsigned int irq);
84
85 static unsigned int startup_pint_irq(unsigned int irq)
86 {
87         enable_pint_irq(irq);
88         return 0; /* never anything pending */
89 }
90
91 static struct hw_interrupt_type pint_irq_type = {
92         .typename = "PINT-IRQ",
93         .startup = startup_pint_irq,
94         .shutdown = shutdown_pint_irq,
95         .enable = enable_pint_irq,
96         .disable = disable_pint_irq,
97         .ack = mask_and_ack_pint,
98         .end = end_pint_irq
99 };
100
101 static void disable_pint_irq(unsigned int irq)
102 {
103         unsigned long val;
104
105         val = ctrl_inw(INTC_INTER);
106         val &= ~(1 << (irq - PINT_IRQ_BASE));
107         ctrl_outw(val, INTC_INTER);     /* disable PINTn */
108         portcr_mask &= ~(3 << (irq - PINT_IRQ_BASE)*2);
109 }
110
111 static void enable_pint_irq(unsigned int irq)
112 {
113         unsigned long val;
114
115         val = ctrl_inw(INTC_INTER);
116         val |= 1 << (irq - PINT_IRQ_BASE);
117         ctrl_outw(val, INTC_INTER);     /* enable PINTn */
118         portcr_mask |= 3 << (irq - PINT_IRQ_BASE)*2;
119 }
120
121 static void mask_and_ack_pint(unsigned int irq)
122 {
123         disable_pint_irq(irq);
124 }
125
126 static void end_pint_irq(unsigned int irq)
127 {
128         if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
129                 enable_pint_irq(irq);
130 }
131
132 void make_pint_irq(unsigned int irq)
133 {
134         disable_irq_nosync(irq);
135         irq_desc[irq].chip = &pint_irq_type;
136         disable_pint_irq(irq);
137 }
138
139 static struct ipr_data pint_ipr_map[] = {
140         { PINT0_IRQ, PINT0_IPR_ADDR, PINT0_IPR_POS, PINT0_PRIORITY },
141         { PINT8_IRQ, PINT8_IPR_ADDR, PINT8_IPR_POS, PINT8_PRIORITY },
142 };
143
144 void __init init_IRQ_pint(void)
145 {
146         int i;
147
148         make_ipr_irq(pint_ipr_map, ARRAY_SIZE(pint_ipr_map));
149
150         enable_irq(PINT0_IRQ);
151         enable_irq(PINT8_IRQ);
152
153         for(i = 0; i < 16; i++)
154                 make_pint_irq(PINT_IRQ_BASE + i);
155
156         for(i = 0; i < 256; i++) {
157                 if (i & 1)
158                         pint_map[i] = 0;
159                 else if (i & 2)
160                         pint_map[i] = 1;
161                 else if (i & 4)
162                         pint_map[i] = 2;
163                 else if (i & 8)
164                         pint_map[i] = 3;
165                 else if (i & 0x10)
166                         pint_map[i] = 4;
167                 else if (i & 0x20)
168                         pint_map[i] = 5;
169                 else if (i & 0x40)
170                         pint_map[i] = 6;
171                 else if (i & 0x80)
172                         pint_map[i] = 7;
173         }
174 }
175
176 int ipr_irq_demux(int irq)
177 {
178         unsigned long creg, dreg, d, sav;
179
180         if (irq == PINT0_IRQ) {
181 #if defined(CONFIG_CPU_SUBTYPE_SH7705) || defined(CONFIG_CPU_SUBTYPE_SH7707)
182                 creg = PORT_PACR;
183                 dreg = PORT_PADR;
184 #else
185                 creg = PORT_PCCR;
186                 dreg = PORT_PCDR;
187 #endif
188                 sav = ctrl_inw(creg);
189                 ctrl_outw(sav | portcr_mask, creg);
190                 d = (~ctrl_inb(dreg) ^ ctrl_inw(INTC_ICR2)) &
191                         ctrl_inw(INTC_INTER) & 0xff;
192                 ctrl_outw(sav, creg);
193
194                 if (d == 0)
195                         return irq;
196
197                 return PINT_IRQ_BASE + pint_map[d];
198         } else if (irq == PINT8_IRQ) {
199 #if defined(CONFIG_CPU_SUBTYPE_SH7705) || defined(CONFIG_CPU_SUBTYPE_SH7707)
200                 creg = PORT_PBCR;
201                 dreg = PORT_PBDR;
202 #else
203                 creg = PORT_PFCR;
204                 dreg = PORT_PFDR;
205 #endif
206                 sav = ctrl_inw(creg);
207                 ctrl_outw(sav | (portcr_mask >> 16), creg);
208                 d = (~ctrl_inb(dreg) ^ (ctrl_inw(INTC_ICR2) >> 8)) &
209                         (ctrl_inw(INTC_INTER) >> 8) & 0xff;
210                 ctrl_outw(sav, creg);
211
212                 if (d == 0)
213                         return irq;
214
215                 return PINT_IRQ_BASE + 8 + pint_map[d];
216         }
217
218         return irq;
219 }
220