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