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 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 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 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 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 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 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 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 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 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