Home | History | Annotate | Line # | Download | only in ingenic
intr.c revision 1.9
      1 /*	$NetBSD: intr.c,v 1.9 2015/04/04 13:06:01 macallan 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.9 2015/04/04 13:06:01 macallan Exp $");
     31 
     32 #define __INTR_PRIVATE
     33 
     34 #include <sys/param.h>
     35 #include <sys/cpu.h>
     36 #include <sys/device.h>
     37 #include <sys/kernel.h>
     38 #include <sys/systm.h>
     39 #include <sys/timetc.h>
     40 #include <sys/bitops.h>
     41 
     42 #include <mips/locore.h>
     43 #include <machine/intr.h>
     44 
     45 #include <mips/ingenic/ingenic_regs.h>
     46 
     47 #include "opt_ingenic.h"
     48 
     49 extern void ingenic_clockintr(uint32_t);
     50 extern void ingenic_puts(const char *);
     51 
     52 /*
     53  * This is a mask of bits to clear in the SR when we go to a
     54  * given hardware interrupt priority level.
     55  */
     56 static const struct ipl_sr_map ingenic_ipl_sr_map = {
     57     .sr_bits = {
     58 	[IPL_NONE] =		0,
     59 	[IPL_SOFTCLOCK] =	MIPS_SOFT_INT_MASK_0,
     60 	[IPL_SOFTNET] =		MIPS_SOFT_INT_MASK_0 | MIPS_SOFT_INT_MASK_1,
     61 	[IPL_VM] =
     62 	    MIPS_SOFT_INT_MASK_0 | MIPS_SOFT_INT_MASK_1 |
     63 	    MIPS_INT_MASK_0 |
     64 	    MIPS_INT_MASK_3 |
     65 	    MIPS_INT_MASK_4 |
     66 	    MIPS_INT_MASK_5,
     67 	[IPL_SCHED] =
     68 	    MIPS_SOFT_INT_MASK_0 | MIPS_SOFT_INT_MASK_1 |
     69 	    MIPS_INT_MASK_0 |
     70 	    MIPS_INT_MASK_1 |
     71 	    MIPS_INT_MASK_2 |
     72 	    MIPS_INT_MASK_3 |
     73 	    MIPS_INT_MASK_4 |
     74 	    MIPS_INT_MASK_5,
     75 	[IPL_DDB] =		MIPS_INT_MASK,
     76 	[IPL_HIGH] =            MIPS_INT_MASK,
     77     },
     78 };
     79 
     80 #define NINTR 64
     81 
     82 /* some timer channels share interrupts, couldn't find any others */
     83 struct intrhand {
     84 	struct evcnt ih_count;
     85 	char ih_name[16];
     86 	int (*ih_func)(void *);
     87 	void *ih_arg;
     88 	int ih_ipl;
     89 };
     90 
     91 struct intrhand intrs[NINTR];
     92 struct evcnt clockintrs;
     93 
     94 void ingenic_irq(int);
     95 
     96 void
     97 evbmips_intr_init(void)
     98 {
     99 	uint32_t reg;
    100 	int i;
    101 
    102 	ipl_sr_map = ingenic_ipl_sr_map;
    103 
    104 	evcnt_attach_dynamic(&clockintrs,
    105 	    EVCNT_TYPE_INTR, NULL, "timer", "intr");
    106 
    107 	/* zero all handlers */
    108 	for (i = 0; i < NINTR; i++) {
    109 		intrs[i].ih_func = NULL;
    110 		intrs[i].ih_arg = NULL;
    111 		snprintf(intrs[i].ih_name, sizeof(intrs[i].ih_name),
    112 		    "irq %d", i);
    113 		evcnt_attach_dynamic(&intrs[i].ih_count, EVCNT_TYPE_INTR,
    114 		    NULL, "INTC", intrs[i].ih_name);
    115 	}
    116 
    117 	/* mask all peripheral IRQs */
    118 	writereg(JZ_ICMR0, 0xffffffff);
    119 	writereg(JZ_ICMR1, 0xffffffff);
    120 
    121 	/* allow peripheral interrupts to core 0 only */
    122 	reg = MFC0(12, 4);	/* reset entry and interrupts */
    123 	reg &= 0xffff0000;
    124 	reg |= REIM_IRQ0_M | REIM_MIRQ0_M;
    125 	MTC0(reg, 12, 4);
    126 }
    127 
    128 void
    129 evbmips_iointr(int ipl, vaddr_t pc, uint32_t ipending)
    130 {
    131 	uint32_t id;
    132 #ifdef INGENIC_INTR_DEBUG
    133 	char buffer[256];
    134 
    135 #if 0
    136 	snprintf(buffer, 256, "pending: %08x CR %08x\n", ipending,
    137 	    MFC0(MIPS_COP_0_CAUSE, 0));
    138 	ingenic_puts(buffer);
    139 #endif
    140 #endif
    141 	/* see which core we're on */
    142 	id = MFC0(15, 1) & 7;
    143 
    144 	/*
    145 	 * XXX
    146 	 * the manual counts the softint bits as INT0 and INT1, out headers
    147 	 * don't so everything here looks off by two
    148 	 */
    149 	if (ipending & MIPS_INT_MASK_1) {
    150 		/*
    151 		 * this is a mailbox interrupt / IPI
    152 		 */
    153 		uint32_t reg;
    154 
    155 		/* read pending IPIs */
    156 		reg = MFC0(12, 3);
    157 		if (id == 0) {
    158 			if (reg & CS_MIRQ0_P) {
    159 #ifdef MULTIPROCESSOR
    160 				uint32_t tag;
    161 				tag = MFC0(CP0_CORE_MBOX, 0);
    162 
    163 				ipi_process(curcpu(), tag);
    164 #ifdef INGENIC_INTR_DEBUG
    165 				snprintf(buffer, 256,
    166 				    "IPI for core 0, msg %08x\n", tag);
    167 				ingenic_puts(buffer);
    168 #endif
    169 #endif
    170 				reg &= (~CS_MIRQ0_P);
    171 				/* clear it */
    172 				MTC0(reg, 12, 3);
    173 			}
    174 		} else if (id == 1) {
    175 			if (reg & CS_MIRQ1_P) {
    176 #ifdef MULTIPROCESSOR
    177 				uint32_t tag;
    178 				tag = MFC0(CP0_CORE_MBOX, 1);
    179 				ipi_process(curcpu(), tag);
    180 #ifdef INGENIC_INTR_DEBUG
    181 				snprintf(buffer, 256,
    182 				    "IPI for core 1, msg %08x\n", tag);
    183 				ingenic_puts(buffer);
    184 #endif
    185 #endif
    186 				reg &= (~CS_MIRQ1_P);
    187 				/* clear it */
    188 				MTC0(reg, 12, 3);
    189 			}
    190 		}
    191 	}
    192 	if (ipending & MIPS_INT_MASK_2) {
    193 		/* this is a timer interrupt */
    194 		ingenic_clockintr(id);
    195 		clockintrs.ev_count++;
    196 		ingenic_puts("INT2\n");
    197 	}
    198 	if (ipending & MIPS_INT_MASK_0) {
    199 		uint32_t mask;
    200 		/* peripheral interrupt */
    201 
    202 		/*
    203 		 * XXX
    204 		 * OS timer interrupts are supposed to show up as INT2 as well
    205 		 * but I haven't seen them there so for now we just weed them
    206 		 * out right here.
    207 		 * The idea is to allow peripheral interrupts on both cores but
    208 		 * block INT0 on core1 so it would see only timer interrupts
    209 		 * and IPIs. If that doesn't work we'll have to send an IPI to
    210 		 * core1 for each timer tick.
    211 		 */
    212 		mask = readreg(JZ_ICPR0);
    213 		if (mask & 0x0c000000) {
    214 			writereg(JZ_ICMSR0, mask);
    215 			ingenic_clockintr(id);
    216 			writereg(JZ_ICMCR0, mask);
    217 			clockintrs.ev_count++;
    218 		}
    219 		ingenic_irq(ipl);
    220 		KASSERT(id == 0);
    221 	}
    222 }
    223 
    224 void
    225 ingenic_irq(int ipl)
    226 {
    227 	uint32_t irql, irqh, mask, ll, hh;
    228 	int bit, idx, bail;
    229 #ifdef INGENIC_INTR_DEBUG
    230 	char buffer[16];
    231 #endif
    232 
    233 	irql = readreg(JZ_ICPR0);
    234 	irqh = readreg(JZ_ICPR1);
    235 #ifdef INGENIC_INTR_DEBUG
    236 	if (irql != 0) {
    237 		snprintf(buffer, 16, " il%08x", irql);
    238 		ingenic_puts(buffer);
    239 	}
    240 #endif
    241 	bail = 32;
    242 	ll = irql;
    243 	hh = irqh;
    244 	writereg(JZ_ICMSR0, ll);
    245 	writereg(JZ_ICMSR1, hh);
    246 	bit = ffs32(irql);
    247 	while (bit != 0) {
    248 		idx = bit - 1;
    249 		mask = 1 << idx;
    250 		intrs[idx].ih_count.ev_count++;
    251 		if (intrs[idx].ih_func != NULL) {
    252 			if (intrs[idx].ih_ipl == IPL_VM)
    253 				KERNEL_LOCK(1, NULL);
    254 			intrs[idx].ih_func(intrs[idx].ih_arg);
    255 			if (intrs[idx].ih_ipl == IPL_VM)
    256 				KERNEL_UNLOCK_ONE(NULL);
    257 		} else {
    258 			/* spurious interrupt, mask it */
    259 			writereg(JZ_ICMSR0, mask);
    260 		}
    261 		irql &= ~mask;
    262 		bit = ffs32(irql);
    263 		bail--;
    264 		KASSERT(bail > 0);
    265 	}
    266 
    267 #ifdef INGENIC_INTR_DEBUG
    268 	if (irqh != 0) {
    269 		snprintf(buffer, 16, " ih%08x", irqh);
    270 		ingenic_puts(buffer);
    271 	}
    272 #endif
    273 	bit = ffs32(irqh);
    274 	while (bit != 0) {
    275 		idx = bit - 1;
    276 		mask = 1 << idx;
    277 		idx += 32;
    278 		intrs[idx].ih_count.ev_count++;
    279 		if (intrs[idx].ih_func != NULL) {
    280 			if (intrs[idx].ih_ipl == IPL_VM)
    281 				KERNEL_LOCK(1, NULL);
    282 			intrs[idx].ih_func(intrs[idx].ih_arg);
    283 			if (intrs[idx].ih_ipl == IPL_VM)
    284 				KERNEL_UNLOCK_ONE(NULL);
    285 		} else {
    286 			/* spurious interrupt, mask it */
    287 			writereg(JZ_ICMSR1, mask);
    288 		}
    289 		irqh &= ~mask;
    290 		bit = ffs32(irqh);
    291 	}
    292 	writereg(JZ_ICMCR0, ll);
    293 	writereg(JZ_ICMCR1, hh);
    294 }
    295 
    296 void *
    297 evbmips_intr_establish(int irq, int (*func)(void *), void *arg)
    298 {
    299 	int s;
    300 
    301 	if ((irq < 0) || (irq >= NINTR)) {
    302 		aprint_error("%s: invalid irq %d\n", __func__, irq);
    303 		return NULL;
    304 	}
    305 
    306 	s = splhigh();	/* XXX probably needs a mutex */
    307 	intrs[irq].ih_func = func;
    308 	intrs[irq].ih_arg = arg;
    309 	intrs[irq].ih_ipl = IPL_VM;
    310 
    311 	/* now enable the IRQ */
    312 	if (irq >= 32) {
    313 		writereg(JZ_ICMCR1, 1 << (irq - 32));
    314 	} else
    315 		writereg(JZ_ICMCR0, 1 << irq);
    316 
    317 	splx(s);
    318 
    319 	return ((void *)(irq + 1));
    320 }
    321 
    322 void
    323 evbmips_intr_disestablish(void *cookie)
    324 {
    325 	int irq = ((int)cookie) - 1;
    326 	int s;
    327 
    328 	if ((irq < 0) || (irq >= NINTR)) {
    329 		aprint_error("%s: invalid irq %d\n", __func__, irq);
    330 		return;
    331 	}
    332 
    333 	s = splhigh();
    334 
    335 	/* disable the IRQ */
    336 	if (irq >= 32) {
    337 		writereg(JZ_ICMSR1, 1 << (irq - 32));
    338 	} else
    339 		writereg(JZ_ICMSR0, 1 << irq);
    340 
    341 	intrs[irq].ih_func = NULL;
    342 	intrs[irq].ih_arg = NULL;
    343 	intrs[irq].ih_ipl = 0;
    344 
    345 	splx(s);
    346 }
    347