Home | History | Annotate | Line # | Download | only in newsmips
      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