xref: /netbsd/sys/arch/newsmips/newsmips/news3400.c (revision 78f344db)
1 /*	$NetBSD: news3400.c,v 1.25 2016/11/14 19:24:23 maya Exp $	*/
2 
3 /*-
4  * Copyright (C) 1999 Tsubai Masanari.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: news3400.c,v 1.25 2016/11/14 19:24:23 maya Exp $");
31 
32 #define __INTR_PRIVATE
33 #include <sys/param.h>
34 #include <sys/kernel.h>
35 #include <sys/proc.h>
36 #include <sys/systm.h>
37 #include <sys/cpu.h>
38 #include <sys/intr.h>
39 
40 #include <mips/locore.h>
41 
42 #include <machine/adrsmap.h>
43 #include <machine/psl.h>
44 #include <newsmips/newsmips/machid.h>
45 
46 #include <newsmips/dev/hbvar.h>
47 
48 int news3400_badaddr(void *, u_int);
49 
50 static void news3400_level0_intr(void);
51 static void news3400_level1_intr(void);
52 static void news3400_enable_intr(void);
53 static void news3400_disable_intr(void);
54 static void news3400_enable_timer(void);
55 static void news3400_readidrom(uint8_t *);
56 
57 static volatile int badaddr_flag;
58 
59 #define INT_MASK_FPU MIPS_INT_MASK_3
60 
61 /*
62  * This is a mask of bits to clear in the SR when we go to a
63  * given interrupt priority level.
64  */
65 static const struct ipl_sr_map news3400_ipl_sr_map = {
66     .sr_bits = {
67 	[IPL_NONE] =		0,
68 	[IPL_SOFTCLOCK] =	MIPS_SOFT_INT_MASK_0,
69 	[IPL_SOFTNET] =		MIPS_SOFT_INT_MASK,
70 	[IPL_VM] =		MIPS_SOFT_INT_MASK
71 				| MIPS_INT_MASK_0
72 				| MIPS_INT_MASK_1,
73 	[IPL_SCHED] =		MIPS_SOFT_INT_MASK
74 				| MIPS_INT_MASK_0
75 				| MIPS_INT_MASK_1
76 				| MIPS_INT_MASK_2,
77 	[IPL_DDB] =		MIPS_INT_MASK,
78 	[IPL_HIGH] =		MIPS_INT_MASK,
79     },
80 };
81 
82 /*
83  * Handle news3400 interrupts.
84  */
85 void
news3400_intr(int ppl,uint32_t pc,uint32_t status)86 news3400_intr(int ppl, uint32_t pc, uint32_t status)
87 {
88 	uint32_t ipending;
89 	int ipl;
90 
91 	while (ppl < (ipl = splintr(&ipending))) {
92 
93 	/* handle clock interrupts ASAP */
94 		if (ipending & MIPS_INT_MASK_2) {
95 			int stat;
96 
97 			stat = *(volatile uint8_t *)INTST0;
98 			stat &= INTST0_TIMINT|INTST0_KBDINT|INTST0_MSINT;
99 
100 			*(volatile uint8_t *)INTCLR0 = stat;
101 			if (stat & INTST0_TIMINT) {
102 				struct clockframe cf = {
103 					.pc = pc,
104 					.sr = status,
105 					.intr = (curcpu()->ci_idepth > 1),
106 				};
107 				hardclock(&cf);
108 				intrcnt[HARDCLOCK_INTR]++;
109 			}
110 
111 			if (stat)
112 				hb_intr_dispatch(2, stat);
113 
114 		}
115 
116 		if (ipending & MIPS_INT_MASK_5) {
117 			*(volatile uint8_t *)INTCLR0 = INTCLR0_PERR;
118 			printf("Memory error interrupt(?) at 0x%x\n", pc);
119 		}
120 
121 		/* asynchronous bus error */
122 		if (ipending & MIPS_INT_MASK_4) {
123 			*(volatile uint8_t *)INTCLR0 = INTCLR0_BERR;
124 			badaddr_flag = 1;
125 		}
126 
127 		if (ipending & MIPS_INT_MASK_1) {
128 			news3400_level1_intr();
129 		}
130 
131 		if (ipending & MIPS_INT_MASK_0) {
132 			news3400_level0_intr();
133 		}
134 
135 		/* FPU notification */
136 		if (ipending & INT_MASK_FPU) {
137 			if (!USERMODE(status))
138 				panic("kernel used FPU: PC %x, SR %x",
139 				      pc, status);
140 
141 			intrcnt[FPU_INTR]++;
142 #if !defined(FPEMUL)
143 			mips_fpu_intr(pc, curlwp->l_md.md_utf);
144 #endif
145 		}
146 	}
147 }
148 
149 #define LEVEL0_MASK \
150 	(INTST1_DMA|INTST1_SLOT1|INTST1_SLOT3|INTST1_EXT1|INTST1_EXT3)
151 
152 static void
news3400_level0_intr(void)153 news3400_level0_intr(void)
154 {
155 	volatile uint8_t *intst1 = (void *)INTST1;
156 	volatile uint8_t *intclr1 = (void *)INTCLR1;
157 	uint8_t stat;
158 
159 	stat = *intst1 & LEVEL0_MASK;
160 	*intclr1 = stat;
161 
162 	hb_intr_dispatch(0, stat);
163 
164 	if (stat & INTST1_SLOT1)
165 		intrcnt[SLOT1_INTR]++;
166 	if (stat & INTST1_SLOT3)
167 		intrcnt[SLOT3_INTR]++;
168 }
169 
170 #define LEVEL1_MASK0	(INTST0_CFLT|INTST0_CBSY)
171 #define LEVEL1_MASK1	(INTST1_BEEP|INTST1_SCC|INTST1_LANCE)
172 
173 static void
news3400_level1_intr(void)174 news3400_level1_intr(void)
175 {
176 	volatile uint8_t *inten1 = (void *)INTEN1;
177 	volatile uint8_t *intst1 = (void *)INTST1;
178 	volatile uint8_t *intclr1 = (void *)INTCLR1;
179 	uint8_t stat1, saved_inten1;
180 
181 	saved_inten1 = *inten1;
182 
183 	*inten1 = 0;		/* disable BEEP, LANCE, and SCC */
184 
185 	stat1 = *intst1 & LEVEL1_MASK1;
186 	*intclr1 = stat1;
187 
188 	stat1 &= saved_inten1;
189 
190 	hb_intr_dispatch(1, stat1);
191 
192 	*inten1 = saved_inten1;
193 
194 	if (stat1 & INTST1_SCC)
195 		intrcnt[SERIAL0_INTR]++;
196 	if (stat1 & INTST1_LANCE)
197 		intrcnt[LANCE_INTR]++;
198 }
199 
200 int
news3400_badaddr(void * addr,u_int size)201 news3400_badaddr(void *addr, u_int size)
202 {
203 	uint32_t cause;
204 	volatile u_int x;
205 	volatile uint8_t *intclr0 = (void *)INTCLR0;
206 
207 	badaddr_flag = 0;
208 
209 	/* clear bus error interrupt */
210 	*intclr0 = INTCLR0_BERR;
211 
212 	/* bus error will cause INT4 */
213 	switch (size) {
214 	case 1:
215 		x = *(volatile uint8_t *)addr;
216 		break;
217 	case 2:
218 		x = *(volatile uint16_t *)addr;
219 		break;
220 	case 4:
221 		x = *(volatile uint32_t *)addr;
222 		break;
223 	}
224 	__USE(x);
225 
226 	/* also check CPU INT4 here for bus errors during splhigh() */
227 	if (badaddr_flag == 0) {
228 		cause = mips_cp0_cause_read();
229 		if ((cause & MIPS_INT_MASK_4) != 0) {
230 			badaddr_flag = 1;
231 			*intclr0 = INTCLR0_BERR;
232 		}
233 	}
234 
235 	return badaddr_flag;
236 }
237 
238 static void
news3400_enable_intr(void)239 news3400_enable_intr(void)
240 {
241 	volatile uint8_t *inten0 = (void *)INTEN0;
242 	volatile uint8_t *inten1 = (void *)INTEN1;
243 	volatile uint8_t *intclr0 = (void *)INTCLR0;
244 	volatile uint8_t *intclr1 = (void *)INTCLR1;
245 
246 	/* clear all interrupts */
247 	*intclr0 = 0xff;
248 	*intclr1 = 0xff;
249 
250 	/*
251 	 * It's not a time to enable timer yet.
252 	 *
253 	 *	INTEN0:  PERR ABORT BERR TIMER KBD  MS    CFLT CBSY
254 	 *		  o     o    o     x    o    o     x    x
255 	 *	INTEN1:  BEEP SCC  LANCE DMA  SLOT1 SLOT3 EXT1 EXT3
256 	 *		  x     o    o     o    o    o     x    x
257 	 */
258 
259 	*inten0 = INTEN0_PERR | INTEN0_ABORT | INTEN0_BERR |
260 		  INTEN0_KBDINT | INTEN0_MSINT;
261 
262 	*inten1 = INTEN1_SCC | INTEN1_LANCE | INTEN1_DMA |
263 		  INTEN1_SLOT1 | INTEN1_SLOT3;
264 }
265 
266 static void
news3400_disable_intr(void)267 news3400_disable_intr(void)
268 {
269 
270 	volatile uint8_t *inten0 = (void *)INTEN0;
271 	volatile uint8_t *inten1 = (void *)INTEN1;
272 
273 	/* always enable bus error check so that news3400_badaddr() works */
274 	*inten0 = INTEN0_BERR;
275 	*inten1 = 0;
276 }
277 
278 static void
news3400_enable_timer(void)279 news3400_enable_timer(void)
280 {
281 
282 	/* initialize interval timer */
283 	*(volatile uint8_t *)ITIMER = IOCLOCK / 6144 / 100 - 1;
284 
285 	/* enable timer interrupt */
286 	*(volatile uint8_t *)INTEN0 |= (uint8_t)INTEN0_TIMINT;
287 }
288 
289 static void
news3400_readidrom(uint8_t * rom)290 news3400_readidrom(uint8_t *rom)
291 {
292 	uint8_t *p = (uint8_t *)IDROM;
293 	int i;
294 
295 	for (i = 0; i < sizeof (struct idrom); i++, p += 2)
296 		*rom++ = ((*p & 0x0f) << 4) + (*(p + 1) & 0x0f);
297 }
298 
299 extern struct idrom idrom;
300 
301 void
news3400_init(void)302 news3400_init(void)
303 {
304 
305 	ipl_sr_map = news3400_ipl_sr_map;
306 
307 	enable_intr = news3400_enable_intr;
308 	disable_intr = news3400_disable_intr;
309 	enable_timer = news3400_enable_timer;
310 
311 	news3400_readidrom((uint8_t *)&idrom);
312 	hostid = idrom.id_serial;
313 }
314