Home | History | Annotate | Line # | Download | only in at91
      1 /*	$Id: at91aic.c,v 1.13 2022/07/21 10:09:20 andvar Exp $	*/
      2 /*	$NetBSD: at91aic.c,v 1.13 2022/07/21 10:09:20 andvar Exp $	*/
      3 
      4 /*
      5  * Copyright (c) 2007 Embedtronics Oy.
      6  * All rights reserved.
      7  *
      8  * Based on ep93xx_intr.c
      9  * Copyright (c) 2002 The NetBSD Foundation, Inc.
     10  * All rights reserved.
     11  *
     12  * This code is derived from software contributed to The NetBSD Foundation
     13  * by Jesse Off
     14  *
     15  * This code is derived from software contributed to The NetBSD Foundation
     16  * by Ichiro FUKUHARA and Naoto Shimazaki.
     17  *
     18  * Redistribution and use in source and binary forms, with or without
     19  * modification, are permitted provided that the following conditions
     20  * are met:
     21  * 1. Redistributions of source code must retain the above copyright
     22  *    notice, this list of conditions and the following disclaimer.
     23  * 2. Redistributions in binary form must reproduce the above copyright
     24  *    notice, this list of conditions and the following disclaimer in the
     25  *    documentation and/or other materials provided with the distribution.
     26  *
     27  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     28  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     29  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     30  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     31  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     37  * POSSIBILITY OF SUCH DAMAGE.
     38  */
     39 
     40 
     41 /*
     42  * Interrupt support for the Atmel's AT91xx9xxx family controllers
     43  */
     44 
     45 #include <sys/param.h>
     46 #include <sys/systm.h>
     47 #include <sys/kmem.h>
     48 #include <sys/termios.h>
     49 
     50 #include <uvm/uvm_extern.h>
     51 
     52 #include <sys/bus.h>
     53 #include <machine/intr.h>
     54 
     55 #include <arm/cpufunc.h>
     56 
     57 #include <arm/at91/at91reg.h>
     58 #include <arm/at91/at91var.h>
     59 #include <arm/at91/at91aicreg.h>
     60 #include <arm/at91/at91aicvar.h>
     61 
     62 #define	NIRQ	32
     63 
     64 /* Interrupt handler queues. */
     65 struct intrq intrq[NIRQ];
     66 
     67 /* Interrupts to mask at each level. */
     68 static uint32_t aic_imask[NIPL];
     69 
     70 /* Software copy of the IRQs we have enabled. */
     71 volatile uint32_t aic_intr_enabled;
     72 
     73 #define	AICREG(reg)	*((volatile uint32_t*) (AT91AIC_BASE + (reg)))
     74 
     75 static int	at91aic_match(device_t, cfdata_t, void *);
     76 static void	at91aic_attach(device_t, device_t, void *);
     77 
     78 CFATTACH_DECL_NEW(at91aic, 0,
     79 	      at91aic_match, at91aic_attach, NULL, NULL);
     80 
     81 static int
     82 at91aic_match(device_t parent, cfdata_t match, void *aux)
     83 {
     84 	if (strcmp(match->cf_name, "at91aic") == 0)
     85 		return 2;
     86 	return 0;
     87 }
     88 
     89 static void
     90 at91aic_attach(device_t parent, device_t self, void *aux)
     91 {
     92 	int i;
     93 
     94 	(void)parent; (void)self; (void)aux;
     95 	for (i = 0; i < NIRQ; i++) {
     96 		evcnt_attach_dynamic(&intrq[i].iq_ev, EVCNT_TYPE_INTR,
     97 				     NULL, "aic", intrq[i].iq_name);
     98 	}
     99 	printf("\n");
    100 }
    101 
    102 static inline void
    103 at91_set_intrmask(uint32_t aic_irqs)
    104 {
    105 	AICREG(AIC_IDCR)	= aic_irqs;
    106 	AICREG(AIC_IECR)	= aic_intr_enabled & ~aic_irqs;
    107 }
    108 
    109 static inline void
    110 at91_enable_irq(int irq)
    111 {
    112 	aic_intr_enabled       |= (1U << irq);
    113 	AICREG(AIC_IECR)	= (1U << irq);
    114 }
    115 
    116 static inline void
    117 at91_disable_irq(int irq)
    118 {
    119 	aic_intr_enabled       &= ~(1U << irq);
    120 	AICREG(AIC_IDCR)	=  (1U << irq);
    121 }
    122 
    123 /*
    124  * NOTE: This routine must be called with interrupts disabled in the CPSR.
    125  */
    126 static void
    127 at91aic_calculate_masks(void)
    128 {
    129 	struct intrq *iq;
    130 	struct intrhand *ih;
    131 	int irq, ipl;
    132 
    133 	/* First, figure out which IPLs each IRQ has. */
    134 	for (irq = 0; irq < NIRQ; irq++) {
    135 		int levels = 0;
    136 		iq = &intrq[irq];
    137 		at91_disable_irq(irq);
    138 		for (ih = TAILQ_FIRST(&iq->iq_list); ih != NULL;
    139 		     ih = TAILQ_NEXT(ih, ih_list))
    140 			levels |= (1U << ih->ih_ipl);
    141 		iq->iq_levels = levels;
    142 	}
    143 
    144 	/* Next, figure out which IRQs are used by each IPL. */
    145 	for (ipl = 0; ipl < NIPL; ipl++) {
    146 		int aic_irqs = 0;
    147 		for (irq = 0; irq < AIC_NIRQ; irq++) {
    148 			if (intrq[irq].iq_levels & (1U << ipl))
    149 				aic_irqs |= (1U << irq);
    150 		}
    151 		aic_imask[ipl] = aic_irqs;
    152 	}
    153 
    154 	/* IPL_NONE must open up all interrupts */
    155 	KASSERT(aic_imask[IPL_NONE] == 0);
    156 	KASSERT(aic_imask[IPL_SOFTCLOCK] == 0);
    157 	KASSERT(aic_imask[IPL_SOFTBIO] == 0);
    158 	KASSERT(aic_imask[IPL_SOFTNET] == 0);
    159 	KASSERT(aic_imask[IPL_SOFTSERIAL] == 0);
    160 
    161 	/*
    162 	 * Enforce a hierarchy that gives "slow" device (or devices with
    163 	 * limited input buffer space/"real-time" requirements) a better
    164 	 * chance at not dropping data.
    165 	 */
    166 	aic_imask[IPL_SCHED] |= aic_imask[IPL_VM];
    167 	aic_imask[IPL_HIGH] |= aic_imask[IPL_SCHED];
    168 
    169 	/*
    170 	 * Now compute which IRQs must be blocked when servicing any
    171 	 * given IRQ.
    172 	 */
    173 	for (irq = 0; irq < MIN(NIRQ, AIC_NIRQ); irq++) {
    174 		iq = &intrq[irq];
    175 		if (TAILQ_FIRST(&iq->iq_list) != NULL)
    176 			at91_enable_irq(irq);
    177 	}
    178 	/*
    179 	 * update current mask
    180 	 */
    181 	at91_set_intrmask(aic_imask[curcpl()]);
    182 }
    183 
    184 inline void
    185 splx(int new)
    186 {
    187 	int	old;
    188 	u_int	oldirqstate;
    189 
    190 	oldirqstate = disable_interrupts(I32_bit);
    191 	old = curcpl();
    192 	if (old != new) {
    193 		set_curcpl(new);
    194 		at91_set_intrmask(aic_imask[new]);
    195 	}
    196 	restore_interrupts(oldirqstate);
    197 #ifdef __HAVE_FAST_SOFTINTS
    198 	cpu_dosoftints();
    199 #endif
    200 }
    201 
    202 int
    203 _splraise(int ipl)
    204 {
    205 	int	old;
    206 	u_int	oldirqstate;
    207 
    208 	oldirqstate = disable_interrupts(I32_bit);
    209 	old = curcpl();
    210 	if (old != ipl) {
    211 		set_curcpl(ipl);
    212 		at91_set_intrmask(aic_imask[ipl]);
    213 	}
    214 	restore_interrupts(oldirqstate);
    215 
    216 	return (old);
    217 }
    218 
    219 int
    220 _spllower(int ipl)
    221 {
    222 	int	old = curcpl();
    223 
    224 	if (old <= ipl)
    225 		return (old);
    226 	splx(ipl);
    227 #ifdef __HAVE_FAST_SOFTINTS
    228 	cpu_dosoftints();
    229 #endif
    230 	return (old);
    231 }
    232 
    233 /*
    234  * at91aic_init:
    235  *
    236  *	Initialize the rest of the interrupt subsystem, making it
    237  *	ready to handle interrupts from devices.
    238  */
    239 void
    240 at91aic_init(void)
    241 {
    242 	struct intrq *iq;
    243 	int i;
    244 
    245 	aic_intr_enabled = 0;
    246 
    247 	// disable interrupts:
    248 	AICREG(AIC_IDCR)	= -1;
    249 
    250 	for (i = 0; i < NIRQ; i++) {
    251 		iq = &intrq[i];
    252 		TAILQ_INIT(&iq->iq_list);
    253 
    254 		snprintf(iq->iq_name, sizeof(iq->iq_name), "irq %d", i);
    255 	}
    256 
    257 	/* All interrupts should use IRQ not FIQ */
    258 
    259 	AICREG(AIC_IDCR)	= -1;	/* disable interrupts	*/
    260 	AICREG(AIC_ICCR)	= -1;	/* clear all interrupts	*/
    261 	AICREG(AIC_DCR)		= 0;	/* not in debug mode, just to make sure */
    262 	for (i = 0; i < NIRQ; i++) {
    263 	  AICREG(AIC_SMR(i))	= 0;	/* disable interrupt */
    264 	  AICREG(AIC_SVR(i))	= (uint32_t)&intrq[i];	// address of interrupt queue
    265 	}
    266 	AICREG(AIC_FVR)		= 0;	// fast interrupt...
    267 	AICREG(AIC_SPU)		= 0;	// spurious interrupt vector
    268 
    269 	AICREG(AIC_EOICR)	= 0;	/* clear logic... */
    270 	AICREG(AIC_EOICR)	= 0;	/* clear logic... */
    271 
    272 	at91aic_calculate_masks();
    273 
    274 	/* Enable IRQs (don't yet use FIQs). */
    275 	enable_interrupts(I32_bit);
    276 }
    277 
    278 void *
    279 at91aic_intr_establish(int irq, int ipl, int type, int (*ih_func)(void *), void *arg)
    280 {
    281 	struct intrq*		iq;
    282 	struct intrhand*	ih;
    283 	u_int			oldirqstate;
    284 	unsigned		ok;
    285 	uint32_t		smr;
    286 
    287 	if (irq < 0 || irq >= NIRQ)
    288 		panic("intr_establish: IRQ %d out of range", irq);
    289 	if (ipl < 0 || ipl >= NIPL)
    290 		panic("intr_establish: IPL %d out of range", ipl);
    291 
    292 	smr = 1;		// all interrupts have priority one.. ok?
    293 	switch (type) {
    294 	case _INTR_LOW_LEVEL:
    295 		smr |= AIC_SMR_SRCTYPE_LVL_LO;
    296 		break;
    297 	case INTR_HIGH_LEVEL:
    298 		smr |= AIC_SMR_SRCTYPE_LVL_HI;
    299 		break;
    300 	case INTR_FALLING_EDGE:
    301 		smr |= AIC_SMR_SRCTYPE_FALLING;
    302 		break;
    303 	case INTR_RISING_EDGE:
    304 		smr |= AIC_SMR_SRCTYPE_RISING;
    305 		break;
    306 	default:
    307 		panic("intr_establish: interrupt type %d is invalid", type);
    308 	}
    309 
    310 	ih = kmem_alloc(sizeof(*ih), KM_SLEEP);
    311 	if (ih == NULL)
    312 		return (NULL);
    313 
    314 	ih->ih_func = ih_func;
    315 	ih->ih_arg = arg;
    316 	ih->ih_irq = irq;
    317 	ih->ih_ipl = ipl;
    318 
    319 	iq = &intrq[irq];
    320 
    321 	oldirqstate = disable_interrupts(I32_bit);
    322 	if (TAILQ_FIRST(&iq->iq_list) == NULL || (iq->iq_type & ~type) == 0) {
    323 		AICREG(AIC_SMR(irq)) = smr;
    324 		iq->iq_type = type;
    325 		TAILQ_INSERT_TAIL(&iq->iq_list, ih, ih_list);
    326 		at91aic_calculate_masks();
    327 		ok = 1;
    328 	} else
    329 		ok = 0;
    330 	restore_interrupts(oldirqstate);
    331 
    332 	if (ok) {
    333 #ifdef	AT91AIC_DEBUG
    334 		int i;
    335 		printf("\n");
    336 		for (i = 0; i < NIPL; i++) {
    337 			printf("IPL%d: aic_imask=0x%08X\n", i, aic_imask[i]);
    338 		}
    339 #endif
    340 	} else {
    341 		kmem_free(ih, sizeof(*ih));
    342 		ih = NULL;
    343 	}
    344 
    345 	return (ih);
    346 }
    347 
    348 void
    349 at91aic_intr_disestablish(void *cookie)
    350 {
    351 	struct intrhand*	ih = cookie;
    352 	struct intrq*		iq = &intrq[ih->ih_irq];
    353 	u_int			oldirqstate;
    354 
    355 	oldirqstate = disable_interrupts(I32_bit);
    356 	TAILQ_REMOVE(&iq->iq_list, ih, ih_list);
    357 	at91aic_calculate_masks();
    358 	restore_interrupts(oldirqstate);
    359 }
    360 
    361 #include <arm/at91/at91reg.h>
    362 #include <arm/at91/at91dbgureg.h>
    363 #include <arm/at91/at91pdcreg.h>
    364 
    365 static inline void intr_process(struct intrq *iq, int pcpl, struct trapframe *frame);
    366 
    367 static inline void
    368 intr_process(struct intrq *iq, int pcpl, struct trapframe *frame)
    369 {
    370 	struct intrhand*	ih;
    371 	u_int			oldirqstate, intr;
    372 
    373 	intr = iq - intrq;
    374 
    375 	iq->iq_ev.ev_count++;
    376 	curcpu()->ci_data.cpu_nintr++;
    377 
    378 	if ((1U << intr) & aic_imask[pcpl]) {
    379 		panic("interrupt %d should be masked! (aic_imask=0x%X)", intr, aic_imask[pcpl]);
    380 	}
    381 
    382 	if (iq->iq_busy) {
    383 		panic("interrupt %d busy!", intr);
    384 	}
    385 
    386 	iq->iq_busy = 1;
    387 
    388 	for (ih = TAILQ_FIRST(&iq->iq_list); ih != NULL;
    389 	     ih = TAILQ_NEXT(ih, ih_list)) {
    390 		set_curcpl(ih->ih_ipl);
    391 		at91_set_intrmask(aic_imask[ih->ih_ipl]);
    392 		oldirqstate = enable_interrupts(I32_bit);
    393 		(void) (*ih->ih_func)(ih->ih_arg ? ih->ih_arg : frame);
    394 		restore_interrupts(oldirqstate);
    395 	}
    396 
    397 	if (!iq->iq_busy) {
    398 		panic("interrupt %d not busy!", intr);
    399 	}
    400 	iq->iq_busy = 0;
    401 
    402 	set_curcpl(pcpl);
    403 	at91_set_intrmask(aic_imask[pcpl]);
    404 }
    405 
    406 void
    407 at91aic_intr_dispatch(struct trapframe *frame)
    408 {
    409 	struct intrq*		iq;
    410 	int			pcpl = curcpl();
    411 
    412 	iq = (struct intrq *)AICREG(AIC_IVR);	// get current queue
    413 
    414 	// OK, service interrupt
    415 	if (iq)
    416 		intr_process(iq, pcpl, frame);
    417 
    418 	AICREG(AIC_EOICR) = 0;			// end of interrupt
    419 }
    420 
    421 #if 0
    422 void
    423 at91aic_intr_poll(int irq)
    424 {
    425 	u_int		oldirqstate;
    426 	uint32_t	ipr;
    427 	int		pcpl = curcpl();
    428 
    429 	oldirqstate = disable_interrupts(I32_bit);
    430 	ipr = 	AICREG(AIC_IPR);
    431 	if ((ipr & (1U << irq) & ~aic_imask[pcpl]))
    432 		intr_process(&intrq[irq], pcpl, NULL);
    433 	restore_interrupts(oldirqstate);
    434 #ifdef __HAVE_FAST_SOFTINTS
    435 	cpu_dosoftints();
    436 #endif
    437 }
    438 #endif
    439 
    440 void
    441 at91aic_intr_poll(void *ihp, int flags)
    442 {
    443 	struct intrhand* ih = ihp;
    444 	u_int		oldirqstate, irq = ih->ih_irq;
    445 	uint32_t	ipr;
    446 	int		pcpl = curcpl();
    447 
    448 	oldirqstate = disable_interrupts(I32_bit);
    449 	ipr = AICREG(AIC_IPR);
    450 	if ((ipr & (1U << irq))
    451 	    && (flags || !(aic_imask[pcpl] & (1U << irq)))) {
    452 		set_curcpl(ih->ih_ipl);
    453 		at91_set_intrmask(aic_imask[ih->ih_ipl]);
    454 		(void)enable_interrupts(I32_bit);
    455 		(void)(*ih->ih_func)(ih->ih_arg ? ih->ih_arg : NULL);
    456 		(void)disable_interrupts(I32_bit);
    457 		set_curcpl(pcpl);
    458 		at91_set_intrmask(aic_imask[pcpl]);
    459 	}
    460 	restore_interrupts(oldirqstate);
    461 
    462 #ifdef __HAVE_FAST_SOFTINTS
    463 	cpu_dosoftints();
    464 #endif
    465 }
    466