Home | History | Annotate | Line # | Download | only in ingenic
intr.c revision 1.12
      1 /*	$NetBSD: intr.c,v 1.12 2016/08/27 05:52:43 skrll Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2014 Michael Lorenz
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  * POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.12 2016/08/27 05:52:43 skrll Exp $");
     31 
     32 #define __INTR_PRIVATE
     33 
     34 #include "opt_multiprocessor.h"
     35 
     36 #include <sys/param.h>
     37 #include <sys/cpu.h>
     38 #include <sys/device.h>
     39 #include <sys/kernel.h>
     40 #include <sys/systm.h>
     41 #include <sys/timetc.h>
     42 #include <sys/bitops.h>
     43 
     44 #include <mips/locore.h>
     45 #include <machine/intr.h>
     46 
     47 #include <mips/ingenic/ingenic_regs.h>
     48 
     49 #include "opt_ingenic.h"
     50 
     51 #ifdef INGENIC_INTR_DEBUG
     52 #define DPRINTF printf
     53 #else
     54 #define DPRINTF while (0) printf
     55 #endif
     56 
     57 extern void ingenic_clockintr(struct clockframe *);
     58 extern void ingenic_puts(const char *);
     59 /*
     60  * This is a mask of bits to clear in the SR when we go to a
     61  * given hardware interrupt priority level.
     62  */
     63 static const struct ipl_sr_map ingenic_ipl_sr_map = {
     64     .sr_bits = {
     65 	[IPL_NONE] =		0,
     66 	[IPL_SOFTCLOCK] =	MIPS_SOFT_INT_MASK_0,
     67 	[IPL_SOFTNET] =		MIPS_SOFT_INT_MASK_0 | MIPS_SOFT_INT_MASK_1,
     68 	[IPL_VM] =
     69 	    MIPS_SOFT_INT_MASK_0 | MIPS_SOFT_INT_MASK_1 |
     70 	    MIPS_INT_MASK_0 |
     71 	    MIPS_INT_MASK_3 |
     72 	    MIPS_INT_MASK_4 |
     73 	    MIPS_INT_MASK_5,
     74 	[IPL_SCHED] =
     75 	    MIPS_SOFT_INT_MASK_0 | MIPS_SOFT_INT_MASK_1 |
     76 	    MIPS_INT_MASK_0 |
     77 	    MIPS_INT_MASK_1 |
     78 	    MIPS_INT_MASK_2 |
     79 	    MIPS_INT_MASK_3 |
     80 	    MIPS_INT_MASK_4 |
     81 	    MIPS_INT_MASK_5,
     82 	[IPL_DDB] =		MIPS_INT_MASK,
     83 	[IPL_HIGH] =            MIPS_INT_MASK,
     84     },
     85 };
     86 
     87 #define NINTR 64
     88 
     89 /* some timer channels share interrupts, couldn't find any others */
     90 struct intrhand {
     91 	struct evcnt ih_count;
     92 	char ih_name[16];
     93 	int (*ih_func)(void *);
     94 	void *ih_arg;
     95 	int ih_ipl;
     96 };
     97 
     98 struct intrhand intrs[NINTR];
     99 struct evcnt clockintrs;
    100 
    101 void ingenic_irq(int);
    102 
    103 void
    104 evbmips_intr_init(void)
    105 {
    106 	uint32_t reg;
    107 	int i;
    108 
    109 	ipl_sr_map = ingenic_ipl_sr_map;
    110 
    111 	evcnt_attach_dynamic(&clockintrs,
    112 	    EVCNT_TYPE_INTR, NULL, "timer", "intr");
    113 
    114 	/* zero all handlers */
    115 	for (i = 0; i < NINTR; i++) {
    116 		intrs[i].ih_func = NULL;
    117 		intrs[i].ih_arg = NULL;
    118 		snprintf(intrs[i].ih_name, sizeof(intrs[i].ih_name),
    119 		    "irq %d", i);
    120 		evcnt_attach_dynamic(&intrs[i].ih_count, EVCNT_TYPE_INTR,
    121 		    NULL, "INTC", intrs[i].ih_name);
    122 	}
    123 
    124 	/* mask all peripheral IRQs */
    125 	writereg(JZ_ICMR0, 0xffffffff);
    126 	writereg(JZ_ICMR1, 0xffffffff);
    127 
    128 	/* allow peripheral interrupts to core 0 only */
    129 	reg = MFC0(12, 4);	/* reset entry and interrupts */
    130 	reg &= 0xffff0000;
    131 	reg |= REIM_IRQ0_M | REIM_MIRQ0_M;
    132 #ifdef MULTIPROCESSOR
    133 	reg |= REIM_MIRQ1_M;
    134 #endif
    135 	MTC0(reg, 12, 4);
    136 	MTC0(0, 20, 1);	/* ping the 2nd core */
    137 	DPRINTF("%s %08x\n", __func__, reg);
    138 }
    139 
    140 void
    141 evbmips_iointr(int ipl, uint32_t ipending, struct clockframe *cf)
    142 {
    143 	uint32_t id;
    144 #ifdef INGENIC_INTR_DEBUG
    145 	char buffer[256];
    146 
    147 #if 0
    148 	snprintf(buffer, 256, "pending: %08x CR %08x\n", ipending,
    149 	    MFC0(MIPS_COP_0_CAUSE, 0));
    150 	ingenic_puts(buffer);
    151 #endif
    152 #endif
    153 	/* see which core we're on */
    154 	id = MFC0(15, 1) & 7;
    155 
    156 	/*
    157 	 * XXX
    158 	 * the manual counts the softint bits as INT0 and INT1, our headers
    159 	 * don't so everything here looks off by two
    160 	 */
    161 	if (ipending & MIPS_INT_MASK_1) {
    162 		/*
    163 		 * this is a mailbox interrupt / IPI
    164 		 */
    165 		uint32_t reg;
    166 		int s = splsched();
    167 
    168 		/* read pending IPIs */
    169 		reg = MFC0(12, 3);
    170 		if (id == 0) {
    171 			if (reg & CS_MIRQ0_P) {
    172 #ifdef MULTIPROCESSOR
    173 				uint32_t tag;
    174 				tag = MFC0(CP0_CORE_MBOX, 0);
    175 
    176 				ipi_process(curcpu(), tag);
    177 #ifdef INGENIC_INTR_DEBUG
    178 				snprintf(buffer, 256,
    179 				    "IPI for core 0, msg %08x\n", tag);
    180 				ingenic_puts(buffer);
    181 #endif
    182 #endif
    183 				reg &= (~CS_MIRQ0_P);
    184 				/* clear it */
    185 				MTC0(reg, 12, 3);
    186 			}
    187 		} else if (id == 1) {
    188 			if (reg & CS_MIRQ1_P) {
    189 #ifdef MULTIPROCESSOR
    190 				uint32_t tag;
    191 				tag = MFC0(CP0_CORE_MBOX, 1);
    192 				ingenic_puts("1");
    193 				if (tag & 0x400)
    194 					hardclock(cf);
    195 				//ipi_process(curcpu(), tag);
    196 #ifdef INGENIC_INTR_DEBUG
    197 				snprintf(buffer, 256,
    198 				    "IPI for core 1, msg %08x\n", tag);
    199 				ingenic_puts(buffer);
    200 #endif
    201 #endif
    202 				reg &= (~CS_MIRQ1_P);
    203 				/* clear it */
    204 				MTC0(reg, 12, 3);
    205 			}
    206 		}
    207 		splx(s);
    208 	}
    209 	if (ipending & MIPS_INT_MASK_2) {
    210 		/* this is a timer interrupt */
    211 		ingenic_clockintr(cf);
    212 		clockintrs.ev_count++;
    213 		ingenic_puts("INT2\n");
    214 	}
    215 	if (ipending & MIPS_INT_MASK_0) {
    216 		uint32_t mask;
    217 		/* peripheral interrupt */
    218 
    219 		/*
    220 		 * XXX
    221 		 * OS timer interrupts are supposed to show up as INT2 as well
    222 		 * but I haven't seen them there so for now we just weed them
    223 		 * out right here.
    224 		 * The idea is to allow peripheral interrupts on both cores but
    225 		 * block INT0 on core1 so it would see only timer interrupts
    226 		 * and IPIs. If that doesn't work we'll have to send an IPI to
    227 		 * core1 for each timer tick.
    228 		 */
    229 		mask = readreg(JZ_ICPR0);
    230 		if (mask & 0x0c000000) {
    231 			writereg(JZ_ICMSR0, 0x0c000000);
    232 			ingenic_clockintr(cf);
    233 			writereg(JZ_ICMCR0, 0x0c000000);
    234 			clockintrs.ev_count++;
    235 		}
    236 		ingenic_irq(ipl);
    237 		KASSERT(id == 0);
    238 	}
    239 }
    240 
    241 void
    242 ingenic_irq(int ipl)
    243 {
    244 	uint32_t irql, irqh, mask, ll, hh;
    245 	int bit, idx, bail;
    246 #ifdef INGENIC_INTR_DEBUG
    247 	char buffer[16];
    248 #endif
    249 
    250 	irql = readreg(JZ_ICPR0);
    251 	irqh = readreg(JZ_ICPR1);
    252 #ifdef INGENIC_INTR_DEBUG
    253 	if (irql != 0) {
    254 		snprintf(buffer, 16, " il%08x", irql);
    255 		ingenic_puts(buffer);
    256 	}
    257 #endif
    258 	bail = 32;
    259 	ll = irql;
    260 	hh = irqh;
    261 	writereg(JZ_ICMSR0, ll);
    262 	writereg(JZ_ICMSR1, hh);
    263 	bit = ffs32(irql);
    264 	while (bit != 0) {
    265 		idx = bit - 1;
    266 		mask = 1 << idx;
    267 		intrs[idx].ih_count.ev_count++;
    268 		if (intrs[idx].ih_func != NULL) {
    269 			if (intrs[idx].ih_ipl == IPL_VM)
    270 				KERNEL_LOCK(1, NULL);
    271 			intrs[idx].ih_func(intrs[idx].ih_arg);
    272 			if (intrs[idx].ih_ipl == IPL_VM)
    273 				KERNEL_UNLOCK_ONE(NULL);
    274 		} else {
    275 			/* spurious interrupt, mask it */
    276 			writereg(JZ_ICMSR0, mask);
    277 		}
    278 		irql &= ~mask;
    279 		bit = ffs32(irql);
    280 		bail--;
    281 		KASSERT(bail > 0);
    282 	}
    283 
    284 #ifdef INGENIC_INTR_DEBUG
    285 	if (irqh != 0) {
    286 		snprintf(buffer, 16, " ih%08x", irqh);
    287 		ingenic_puts(buffer);
    288 	}
    289 #endif
    290 	bit = ffs32(irqh);
    291 	while (bit != 0) {
    292 		idx = bit - 1;
    293 		mask = 1 << idx;
    294 		idx += 32;
    295 		intrs[idx].ih_count.ev_count++;
    296 		if (intrs[idx].ih_func != NULL) {
    297 			if (intrs[idx].ih_ipl == IPL_VM)
    298 				KERNEL_LOCK(1, NULL);
    299 			intrs[idx].ih_func(intrs[idx].ih_arg);
    300 			if (intrs[idx].ih_ipl == IPL_VM)
    301 				KERNEL_UNLOCK_ONE(NULL);
    302 		} else {
    303 			/* spurious interrupt, mask it */
    304 			writereg(JZ_ICMSR1, mask);
    305 		}
    306 		irqh &= ~mask;
    307 		bit = ffs32(irqh);
    308 	}
    309 	writereg(JZ_ICMCR0, ll);
    310 	writereg(JZ_ICMCR1, hh);
    311 }
    312 
    313 void *
    314 evbmips_intr_establish(int irq, int (*func)(void *), void *arg)
    315 {
    316 	int s;
    317 
    318 	if ((irq < 0) || (irq >= NINTR)) {
    319 		aprint_error("%s: invalid irq %d\n", __func__, irq);
    320 		return NULL;
    321 	}
    322 
    323 	s = splhigh();	/* XXX probably needs a mutex */
    324 	intrs[irq].ih_func = func;
    325 	intrs[irq].ih_arg = arg;
    326 	intrs[irq].ih_ipl = IPL_VM;
    327 
    328 	/* now enable the IRQ */
    329 	if (irq >= 32) {
    330 		writereg(JZ_ICMCR1, 1 << (irq - 32));
    331 	} else
    332 		writereg(JZ_ICMCR0, 1 << irq);
    333 
    334 	splx(s);
    335 
    336 	return ((void *)(irq + 1));
    337 }
    338 
    339 void
    340 evbmips_intr_disestablish(void *cookie)
    341 {
    342 	int irq = ((int)cookie) - 1;
    343 	int s;
    344 
    345 	if ((irq < 0) || (irq >= NINTR)) {
    346 		aprint_error("%s: invalid irq %d\n", __func__, irq);
    347 		return;
    348 	}
    349 
    350 	s = splhigh();
    351 
    352 	/* disable the IRQ */
    353 	if (irq >= 32) {
    354 		writereg(JZ_ICMSR1, 1 << (irq - 32));
    355 	} else
    356 		writereg(JZ_ICMSR0, 1 << irq);
    357 
    358 	intrs[irq].ih_func = NULL;
    359 	intrs[irq].ih_arg = NULL;
    360 	intrs[irq].ih_ipl = 0;
    361 
    362 	splx(s);
    363 }
    364