1 /*
2  * FILE NAME
3  *	arch/mips/vr41xx/common/giu.c
4  *
5  * BRIEF MODULE DESCRIPTION
6  *	General-purpose I/O Unit Interrupt routines for NEC VR4100 series.
7  *
8  * Author: Yoichi Yuasa
9  *         yyuasa@mvista.com or source@mvista.com
10  *
11  * Copyright 2002 MontaVista Software Inc.
12  *
13  *  This program is free software; you can redistribute it and/or modify it
14  *  under the terms of the GNU General Public License as published by the
15  *  Free Software Foundation; either version 2 of the License, or (at your
16  *  option) any later version.
17  *
18  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
19  *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
24  *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
26  *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
27  *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  *  You should have received a copy of the GNU General Public License along
30  *  with this program; if not, write to the Free Software Foundation, Inc.,
31  *  675 Mass Ave, Cambridge, MA 02139, USA.
32  */
33 /*
34  * Changes:
35  *  MontaVista Software Inc. <yyuasa@mvista.com> or <source@mvista.com>
36  *  - New creation, NEC VR4111, VR4121, VR4122 and VR4131 are supported.
37  *
38  *  Yoichi Yuasa <yuasa@hh.iij4u.or.jp>
39  *  - Added support for NEC VR4133.
40  */
41 #include <linux/errno.h>
42 #include <linux/init.h>
43 #include <linux/irq.h>
44 #include <linux/kernel.h>
45 #include <linux/types.h>
46 
47 #include <asm/cpu.h>
48 #include <asm/io.h>
49 #include <asm/vr41xx/vr41xx.h>
50 
51 #define GIUIOSELL_TYPE1	KSEG1ADDR(0x0b000100)
52 #define GIUIOSELL_TYPE2	KSEG1ADDR(0x0f000140)
53 
54 #define GIUIOSELL	0x00
55 #define GIUIOSELH	0x02
56 #define GIUINTSTATL	0x08
57 #define GIUINTSTATH	0x0a
58 #define GIUINTENL	0x0c
59 #define GIUINTENH	0x0e
60 #define GIUINTTYPL	0x10
61 #define GIUINTTYPH	0x12
62 #define GIUINTALSELL	0x14
63 #define GIUINTALSELH	0x16
64 #define GIUINTHTSELL	0x18
65 #define GIUINTHTSELH	0x1a
66 #define GIUFEDGEINHL	0x20
67 #define GIUFEDGEINHH	0x22
68 #define GIUREDGEINHL	0x24
69 #define GIUREDGEINHH	0x26
70 
71 static uint32_t giu_base;
72 
73 #define read_giuint(offset)		readw(giu_base + (offset))
74 #define write_giuint(val, offset)	writew((val), giu_base + (offset))
75 
set_giuint(uint8_t offset,uint16_t set)76 static inline uint16_t set_giuint(uint8_t offset, uint16_t set)
77 {
78 	uint16_t res;
79 
80 	res = read_giuint(offset);
81 	res |= set;
82 	write_giuint(res, offset);
83 
84 	return res;
85 }
86 
clear_giuint(uint8_t offset,uint16_t clear)87 static inline uint16_t clear_giuint(uint8_t offset, uint16_t clear)
88 {
89 	uint16_t res;
90 
91 	res = read_giuint(offset);
92 	res &= ~clear;
93 	write_giuint(res, offset);
94 
95 	return res;
96 }
97 
vr41xx_enable_giuint(int pin)98 void vr41xx_enable_giuint(int pin)
99 {
100 	if (pin < 16)
101 		set_giuint(GIUINTENL, (uint16_t)1 << pin);
102 	else
103 		set_giuint(GIUINTENH, (uint16_t)1 << (pin - 16));
104 }
105 
vr41xx_disable_giuint(int pin)106 void vr41xx_disable_giuint(int pin)
107 {
108 	if (pin < 16)
109 		clear_giuint(GIUINTENL, (uint16_t)1 << pin);
110 	else
111 		clear_giuint(GIUINTENH, (uint16_t)1 << (pin - 16));
112 }
113 
vr41xx_clear_giuint(int pin)114 void vr41xx_clear_giuint(int pin)
115 {
116 	if (pin < 16)
117 		write_giuint((uint16_t)1 << pin, GIUINTSTATL);
118 	else
119 		write_giuint((uint16_t)1 << (pin - 16), GIUINTSTATH);
120 }
121 
vr41xx_set_irq_trigger(int pin,int trigger,int hold)122 void vr41xx_set_irq_trigger(int pin, int trigger, int hold)
123 {
124 	uint16_t mask;
125 
126 	if (pin < 16) {
127 		mask = (uint16_t)1 << pin;
128 		if (trigger != TRIGGER_LEVEL) {
129         		set_giuint(GIUINTTYPL, mask);
130 			if (hold == SIGNAL_HOLD)
131 				set_giuint(GIUINTHTSELL, mask);
132 			else
133 				clear_giuint(GIUINTHTSELL, mask);
134 			if (current_cpu_data.cputype == CPU_VR4133) {
135 				switch (trigger) {
136 				case TRIGGER_EDGE_FALLING:
137 					set_giuint(GIUFEDGEINHL, mask);
138 					clear_giuint(GIUREDGEINHL, mask);
139 					break;
140 				case TRIGGER_EDGE_RISING:
141 					clear_giuint(GIUFEDGEINHL, mask);
142 					set_giuint(GIUREDGEINHL, mask);
143 					break;
144 				default:
145 					set_giuint(GIUFEDGEINHL, mask);
146 					set_giuint(GIUREDGEINHL, mask);
147 					break;
148 				}
149 			}
150 		} else {
151 			clear_giuint(GIUINTTYPL, mask);
152 			clear_giuint(GIUINTHTSELL, mask);
153 		}
154 	} else {
155 		mask = (uint16_t)1 << (pin - 16);
156 		if (trigger != TRIGGER_LEVEL) {
157 			set_giuint(GIUINTTYPH, mask);
158 			if (hold == SIGNAL_HOLD)
159 				set_giuint(GIUINTHTSELH, mask);
160 			else
161 				clear_giuint(GIUINTHTSELH, mask);
162 			if (current_cpu_data.cputype == CPU_VR4133) {
163 				switch (trigger) {
164 				case TRIGGER_EDGE_FALLING:
165 					set_giuint(GIUFEDGEINHH, mask);
166 					clear_giuint(GIUREDGEINHH, mask);
167 					break;
168 				case TRIGGER_EDGE_RISING:
169 					clear_giuint(GIUFEDGEINHH, mask);
170 					set_giuint(GIUREDGEINHH, mask);
171 					break;
172 				default:
173 					set_giuint(GIUFEDGEINHH, mask);
174 					set_giuint(GIUREDGEINHH, mask);
175 					break;
176 				}
177 			}
178 		} else {
179 			clear_giuint(GIUINTTYPH, mask);
180 			clear_giuint(GIUINTHTSELH, mask);
181 		}
182 	}
183 
184 	vr41xx_clear_giuint(pin);
185 }
186 
vr41xx_set_irq_level(int pin,int level)187 void vr41xx_set_irq_level(int pin, int level)
188 {
189 	uint16_t mask;
190 
191 	if (pin < 16) {
192 		mask = (uint16_t)1 << pin;
193 		if (level == LEVEL_HIGH)
194 			set_giuint(GIUINTALSELL, mask);
195 		else
196 			clear_giuint(GIUINTALSELL, mask);
197 	} else {
198 		mask = (uint16_t)1 << (pin - 16);
199 		if (level == LEVEL_HIGH)
200 			set_giuint(GIUINTALSELH, mask);
201 		else
202 			clear_giuint(GIUINTALSELH, mask);
203 	}
204 
205 	vr41xx_clear_giuint(pin);
206 }
207 
208 #define GIUINT_NR_IRQS		32
209 
210 enum {
211 	GIUINT_NO_CASCADE,
212 	GIUINT_CASCADE
213 };
214 
215 struct vr41xx_giuint_cascade {
216 	unsigned int flag;
217 	int (*get_irq_number)(int irq);
218 };
219 
220 static struct vr41xx_giuint_cascade giuint_cascade[GIUINT_NR_IRQS];
221 static struct irqaction giu_cascade = {no_action, 0, 0, "cascade", NULL, NULL};
222 
no_irq_number(int irq)223 static int no_irq_number(int irq)
224 {
225 	return -EINVAL;
226 }
227 
vr41xx_cascade_irq(unsigned int irq,int (* get_irq_number)(int irq))228 int vr41xx_cascade_irq(unsigned int irq, int (*get_irq_number)(int irq))
229 {
230 	unsigned int pin;
231 	int retval;
232 
233 	if (irq < GIU_IRQ(0) || irq > GIU_IRQ(31))
234 		return -EINVAL;
235 
236 	if(!get_irq_number)
237 		return -EINVAL;
238 
239 	pin = GIU_IRQ_TO_PIN(irq);
240 	giuint_cascade[pin].flag = GIUINT_CASCADE;
241 	giuint_cascade[pin].get_irq_number = get_irq_number;
242 
243 	retval = setup_irq(irq, &giu_cascade);
244 	if (retval) {
245 		giuint_cascade[pin].flag = GIUINT_NO_CASCADE;
246 		giuint_cascade[pin].get_irq_number = no_irq_number;
247 	}
248 
249 	return retval;
250 }
251 
giuint_do_IRQ(int pin,struct pt_regs * regs)252 unsigned int giuint_do_IRQ(int pin, struct pt_regs *regs)
253 {
254 	struct vr41xx_giuint_cascade *cascade;
255 	unsigned int retval = 0;
256 	int giuint_irq, cascade_irq;
257 
258 	disable_irq(GIUINT_CASCADE_IRQ);
259 	cascade = &giuint_cascade[pin];
260 	giuint_irq = GIU_IRQ(pin);
261 	if (cascade->flag == GIUINT_CASCADE) {
262 		cascade_irq = cascade->get_irq_number(giuint_irq);
263 		disable_irq(giuint_irq);
264 		if (cascade_irq > 0)
265 			retval = do_IRQ(cascade_irq, regs);
266 		enable_irq(giuint_irq);
267 	} else
268 		retval = do_IRQ(giuint_irq, regs);
269 	enable_irq(GIUINT_CASCADE_IRQ);
270 
271 	return retval;
272 }
273 
274 void (*board_irq_init)(void) = NULL;
275 
vr41xx_giuint_init(void)276 void __init vr41xx_giuint_init(void)
277 {
278 	int i;
279 
280 	switch (current_cpu_data.cputype) {
281 	case CPU_VR4111:
282 	case CPU_VR4121:
283 		giu_base = GIUIOSELL_TYPE1;
284 		break;
285 	case CPU_VR4122:
286 	case CPU_VR4131:
287 	case CPU_VR4133:
288 		giu_base = GIUIOSELL_TYPE2;
289 		break;
290 	default:
291 		panic("GIU: Unexpected CPU of NEC VR4100 series");
292 		break;
293 	}
294 
295 	for (i = 0; i < GIUINT_NR_IRQS; i++) {
296                 vr41xx_disable_giuint(i);
297 		giuint_cascade[i].flag = GIUINT_NO_CASCADE;
298 		giuint_cascade[i].get_irq_number = no_irq_number;
299 	}
300 
301 	if (setup_irq(GIUINT_CASCADE_IRQ, &giu_cascade))
302 		printk("GIUINT: Can not cascade IRQ %d.\n", GIUINT_CASCADE_IRQ);
303 
304 	if (board_irq_init)
305 		board_irq_init();
306 }
307