1*44c2d104Sjmcneill /* $NetBSD: ep93xx_intr.c,v 1.28 2023/05/02 09:49:33 jmcneill Exp $ */
291e542a4Sjoff
391e542a4Sjoff /*
491e542a4Sjoff * Copyright (c) 2002 The NetBSD Foundation, Inc.
591e542a4Sjoff * All rights reserved.
691e542a4Sjoff *
791e542a4Sjoff * This code is derived from software contributed to The NetBSD Foundation
891e542a4Sjoff * by Jesse Off
991e542a4Sjoff *
1091e542a4Sjoff * This code is derived from software contributed to The NetBSD Foundation
1191e542a4Sjoff * by Ichiro FUKUHARA and Naoto Shimazaki.
1291e542a4Sjoff *
1391e542a4Sjoff * Redistribution and use in source and binary forms, with or without
1491e542a4Sjoff * modification, are permitted provided that the following conditions
1591e542a4Sjoff * are met:
1691e542a4Sjoff * 1. Redistributions of source code must retain the above copyright
1791e542a4Sjoff * notice, this list of conditions and the following disclaimer.
1891e542a4Sjoff * 2. Redistributions in binary form must reproduce the above copyright
1991e542a4Sjoff * notice, this list of conditions and the following disclaimer in the
2091e542a4Sjoff * documentation and/or other materials provided with the distribution.
2191e542a4Sjoff *
2291e542a4Sjoff * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2391e542a4Sjoff * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2491e542a4Sjoff * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2591e542a4Sjoff * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2691e542a4Sjoff * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2791e542a4Sjoff * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2891e542a4Sjoff * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2991e542a4Sjoff * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3091e542a4Sjoff * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3191e542a4Sjoff * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3291e542a4Sjoff * POSSIBILITY OF SUCH DAMAGE.
3391e542a4Sjoff */
3491e542a4Sjoff
3591e542a4Sjoff #include <sys/cdefs.h>
36*44c2d104Sjmcneill __KERNEL_RCSID(0, "$NetBSD: ep93xx_intr.c,v 1.28 2023/05/02 09:49:33 jmcneill Exp $");
3791e542a4Sjoff
3891e542a4Sjoff /*
3991e542a4Sjoff * Interrupt support for the Cirrus Logic EP93XX
4091e542a4Sjoff */
4191e542a4Sjoff
4291e542a4Sjoff #include <sys/param.h>
4391e542a4Sjoff #include <sys/systm.h>
4438fdb085Sthorpej #include <sys/kmem.h>
4591e542a4Sjoff #include <sys/termios.h>
460e0d0b65Sozaki-r #include <sys/lwp.h>
4791e542a4Sjoff
48cf10107dSdyoung #include <sys/bus.h>
49213e0bd3Smatt #include <sys/intr.h>
5091e542a4Sjoff
51213e0bd3Smatt #include <arm/locore.h>
5291e542a4Sjoff
5391e542a4Sjoff #include <arm/ep93xx/ep93xxreg.h>
5491e542a4Sjoff #include <arm/ep93xx/ep93xxvar.h>
5591e542a4Sjoff
5691e542a4Sjoff /* Interrupt handler queues. */
5791e542a4Sjoff struct intrq intrq[NIRQ];
5891e542a4Sjoff
5991e542a4Sjoff /* Interrupts to mask at each level. */
6008a4aba7Sskrll static uint32_t vic1_imask[NIPL];
6108a4aba7Sskrll static uint32_t vic2_imask[NIPL];
6291e542a4Sjoff
6391e542a4Sjoff /* Current interrupt priority level. */
645f1c88d7Sperry volatile int hardware_spl_level;
6591e542a4Sjoff
6691e542a4Sjoff /* Software copy of the IRQs we have enabled. */
6708a4aba7Sskrll volatile uint32_t vic1_intr_enabled;
6808a4aba7Sskrll volatile uint32_t vic2_intr_enabled;
6991e542a4Sjoff
701c15ddd3Sskrll void ep93xx_intr_dispatch(struct trapframe *);
7191e542a4Sjoff
7208a4aba7Sskrll #define VIC1REG(reg) *((volatile uint32_t*) (EP93XX_AHB_VBASE + \
7391e542a4Sjoff EP93XX_AHB_VIC1 + (reg)))
7408a4aba7Sskrll #define VIC2REG(reg) *((volatile uint32_t*) (EP93XX_AHB_VBASE + \
7591e542a4Sjoff EP93XX_AHB_VIC2 + (reg)))
7691e542a4Sjoff
7791e542a4Sjoff static void
ep93xx_set_intrmask(uint32_t vic1_irqs,uint32_t vic2_irqs)7808a4aba7Sskrll ep93xx_set_intrmask(uint32_t vic1_irqs, uint32_t vic2_irqs)
7991e542a4Sjoff {
8091e542a4Sjoff VIC1REG(EP93XX_VIC_IntEnClear) = vic1_irqs;
8191e542a4Sjoff VIC1REG(EP93XX_VIC_IntEnable) = vic1_intr_enabled & ~vic1_irqs;
8291e542a4Sjoff VIC2REG(EP93XX_VIC_IntEnClear) = vic2_irqs;
8391e542a4Sjoff VIC2REG(EP93XX_VIC_IntEnable) = vic2_intr_enabled & ~vic2_irqs;
8491e542a4Sjoff }
8591e542a4Sjoff
8691e542a4Sjoff static void
ep93xx_enable_irq(int irq)8791e542a4Sjoff ep93xx_enable_irq(int irq)
8891e542a4Sjoff {
8991e542a4Sjoff if (irq < VIC_NIRQ) {
9091e542a4Sjoff vic1_intr_enabled |= (1U << irq);
9191e542a4Sjoff VIC1REG(EP93XX_VIC_IntEnable) = (1U << irq);
9291e542a4Sjoff } else {
9391e542a4Sjoff vic2_intr_enabled |= (1U << (irq - VIC_NIRQ));
9491e542a4Sjoff VIC2REG(EP93XX_VIC_IntEnable) = (1U << (irq - VIC_NIRQ));
9591e542a4Sjoff }
9691e542a4Sjoff }
9791e542a4Sjoff
985f1c88d7Sperry static inline void
ep93xx_disable_irq(int irq)9991e542a4Sjoff ep93xx_disable_irq(int irq)
10091e542a4Sjoff {
10191e542a4Sjoff if (irq < VIC_NIRQ) {
10291e542a4Sjoff vic1_intr_enabled &= ~(1U << irq);
10391e542a4Sjoff VIC1REG(EP93XX_VIC_IntEnClear) = (1U << irq);
10491e542a4Sjoff } else {
10591e542a4Sjoff vic2_intr_enabled &= ~(1U << (irq - VIC_NIRQ));
10691e542a4Sjoff VIC2REG(EP93XX_VIC_IntEnClear) = (1U << (irq - VIC_NIRQ));
10791e542a4Sjoff }
10891e542a4Sjoff }
10991e542a4Sjoff
11091e542a4Sjoff /*
11191e542a4Sjoff * NOTE: This routine must be called with interrupts disabled in the CPSR.
11291e542a4Sjoff */
11391e542a4Sjoff static void
ep93xx_intr_calculate_masks(void)11491e542a4Sjoff ep93xx_intr_calculate_masks(void)
11591e542a4Sjoff {
11691e542a4Sjoff struct intrq *iq;
11791e542a4Sjoff struct intrhand *ih;
11891e542a4Sjoff int irq, ipl;
11991e542a4Sjoff
12091e542a4Sjoff /* First, figure out which IPLs each IRQ has. */
12191e542a4Sjoff for (irq = 0; irq < NIRQ; irq++) {
12291e542a4Sjoff int levels = 0;
12391e542a4Sjoff iq = &intrq[irq];
12491e542a4Sjoff ep93xx_disable_irq(irq);
12591e542a4Sjoff for (ih = TAILQ_FIRST(&iq->iq_list); ih != NULL;
12691e542a4Sjoff ih = TAILQ_NEXT(ih, ih_list))
12791e542a4Sjoff levels |= (1U << ih->ih_ipl);
12891e542a4Sjoff iq->iq_levels = levels;
12991e542a4Sjoff }
13091e542a4Sjoff
13191e542a4Sjoff /* Next, figure out which IRQs are used by each IPL. */
13291e542a4Sjoff for (ipl = 0; ipl < NIPL; ipl++) {
13391e542a4Sjoff int vic1_irqs = 0;
13491e542a4Sjoff int vic2_irqs = 0;
13591e542a4Sjoff for (irq = 0; irq < VIC_NIRQ; irq++) {
13691e542a4Sjoff if (intrq[irq].iq_levels & (1U << ipl))
13791e542a4Sjoff vic1_irqs |= (1U << irq);
13891e542a4Sjoff }
13991e542a4Sjoff vic1_imask[ipl] = vic1_irqs;
14091e542a4Sjoff for (irq = 0; irq < VIC_NIRQ; irq++) {
14191e542a4Sjoff if (intrq[irq + VIC_NIRQ].iq_levels & (1U << ipl))
14291e542a4Sjoff vic2_irqs |= (1U << irq);
14391e542a4Sjoff }
14491e542a4Sjoff vic2_imask[ipl] = vic2_irqs;
14591e542a4Sjoff }
14691e542a4Sjoff
1470c0de807Smatt KASSERT(vic1_imask[IPL_NONE] == 0);
1480c0de807Smatt KASSERT(vic2_imask[IPL_NONE] == 0);
149be8e5859Stsutsui KASSERT(vic1_imask[IPL_SOFTCLOCK] == 0);
150be8e5859Stsutsui KASSERT(vic2_imask[IPL_SOFTCLOCK] == 0);
151be8e5859Stsutsui KASSERT(vic1_imask[IPL_SOFTBIO] == 0);
152be8e5859Stsutsui KASSERT(vic2_imask[IPL_SOFTBIO] == 0);
153be8e5859Stsutsui KASSERT(vic1_imask[IPL_SOFTNET] == 0);
154be8e5859Stsutsui KASSERT(vic2_imask[IPL_SOFTNET] == 0);
155be8e5859Stsutsui KASSERT(vic1_imask[IPL_SOFTSERIAL] == 0);
156be8e5859Stsutsui KASSERT(vic2_imask[IPL_SOFTSERIAL] == 0);
15791e542a4Sjoff
15891e542a4Sjoff /*
159be8e5859Stsutsui * splsched() must block anything that uses the scheduler.
16091e542a4Sjoff */
161825088edSmatt vic1_imask[IPL_SCHED] |= vic1_imask[IPL_VM];
162825088edSmatt vic2_imask[IPL_SCHED] |= vic2_imask[IPL_VM];
16391e542a4Sjoff
16491e542a4Sjoff /*
16591e542a4Sjoff * splhigh() must block "everything".
16691e542a4Sjoff */
167825088edSmatt vic1_imask[IPL_HIGH] |= vic1_imask[IPL_SCHED];
168825088edSmatt vic2_imask[IPL_HIGH] |= vic2_imask[IPL_SCHED];
16991e542a4Sjoff
17091e542a4Sjoff /*
17191e542a4Sjoff * Now compute which IRQs must be blocked when servicing any
17291e542a4Sjoff * given IRQ.
17391e542a4Sjoff */
17491e542a4Sjoff for (irq = 0; irq < NIRQ; irq++) {
17591e542a4Sjoff int vic1_irqs;
17691e542a4Sjoff int vic2_irqs;
17791e542a4Sjoff
17891e542a4Sjoff if (irq < VIC_NIRQ) {
17991e542a4Sjoff vic1_irqs = (1U << irq);
18091e542a4Sjoff vic2_irqs = 0;
18191e542a4Sjoff } else {
18291e542a4Sjoff vic1_irqs = 0;
18391e542a4Sjoff vic2_irqs = (1U << (irq - VIC_NIRQ));
18491e542a4Sjoff }
18591e542a4Sjoff iq = &intrq[irq];
18691e542a4Sjoff if (TAILQ_FIRST(&iq->iq_list) != NULL)
18791e542a4Sjoff ep93xx_enable_irq(irq);
18891e542a4Sjoff for (ih = TAILQ_FIRST(&iq->iq_list); ih != NULL;
18991e542a4Sjoff ih = TAILQ_NEXT(ih, ih_list)) {
19091e542a4Sjoff vic1_irqs |= vic1_imask[ih->ih_ipl];
19191e542a4Sjoff vic2_irqs |= vic2_imask[ih->ih_ipl];
19291e542a4Sjoff }
19391e542a4Sjoff iq->iq_vic1_mask = vic1_irqs;
19491e542a4Sjoff iq->iq_vic2_mask = vic2_irqs;
19591e542a4Sjoff }
19691e542a4Sjoff }
19791e542a4Sjoff
1985f1c88d7Sperry inline void
splx(int new)19991e542a4Sjoff splx(int new)
20091e542a4Sjoff {
20191e542a4Sjoff u_int oldirqstate;
20291e542a4Sjoff
20391e542a4Sjoff oldirqstate = disable_interrupts(I32_bit);
204825088edSmatt set_curcpl(new);
20591e542a4Sjoff if (new != hardware_spl_level) {
20691e542a4Sjoff hardware_spl_level = new;
20791e542a4Sjoff ep93xx_set_intrmask(vic1_imask[new], vic2_imask[new]);
20891e542a4Sjoff }
20991e542a4Sjoff restore_interrupts(oldirqstate);
21091e542a4Sjoff
2110c0de807Smatt #ifdef __HAVE_FAST_SOFTINTS
212825088edSmatt cpu_dosoftints();
2130c0de807Smatt #endif
21491e542a4Sjoff }
21591e542a4Sjoff
21691e542a4Sjoff int
_splraise(int ipl)21791e542a4Sjoff _splraise(int ipl)
21891e542a4Sjoff {
21991e542a4Sjoff int old;
22091e542a4Sjoff u_int oldirqstate;
22191e542a4Sjoff
22291e542a4Sjoff oldirqstate = disable_interrupts(I32_bit);
223825088edSmatt old = curcpl();
224825088edSmatt set_curcpl(ipl);
22591e542a4Sjoff restore_interrupts(oldirqstate);
22691e542a4Sjoff return (old);
22791e542a4Sjoff }
22891e542a4Sjoff
22991e542a4Sjoff int
_spllower(int ipl)23091e542a4Sjoff _spllower(int ipl)
23191e542a4Sjoff {
232825088edSmatt int old = curcpl();
23391e542a4Sjoff
23491e542a4Sjoff if (old <= ipl)
23591e542a4Sjoff return (old);
23691e542a4Sjoff splx(ipl);
23791e542a4Sjoff return (old);
23891e542a4Sjoff }
23991e542a4Sjoff
24091e542a4Sjoff /*
24191e542a4Sjoff * ep93xx_intr_init:
24291e542a4Sjoff *
24391e542a4Sjoff * Initialize the rest of the interrupt subsystem, making it
24491e542a4Sjoff * ready to handle interrupts from devices.
24591e542a4Sjoff */
24691e542a4Sjoff void
ep93xx_intr_init(void)24791e542a4Sjoff ep93xx_intr_init(void)
24891e542a4Sjoff {
24991e542a4Sjoff struct intrq *iq;
25091e542a4Sjoff int i;
25191e542a4Sjoff
25291e542a4Sjoff vic1_intr_enabled = 0;
25391e542a4Sjoff vic2_intr_enabled = 0;
25491e542a4Sjoff
25591e542a4Sjoff for (i = 0; i < NIRQ; i++) {
25691e542a4Sjoff iq = &intrq[i];
25791e542a4Sjoff TAILQ_INIT(&iq->iq_list);
25891e542a4Sjoff
259d02e70faSchristos snprintf(iq->iq_name, sizeof(iq->iq_name), "irq %d", i);
26091e542a4Sjoff }
261825088edSmatt curcpu()->ci_intr_depth = 0;
262825088edSmatt set_curcpl(0);
26391e542a4Sjoff hardware_spl_level = 0;
26491e542a4Sjoff
26591e542a4Sjoff /* All interrupts should use IRQ not FIQ */
26691e542a4Sjoff VIC1REG(EP93XX_VIC_IntSelect) = 0;
26791e542a4Sjoff VIC2REG(EP93XX_VIC_IntSelect) = 0;
26891e542a4Sjoff
26991e542a4Sjoff ep93xx_intr_calculate_masks();
27091e542a4Sjoff
27191e542a4Sjoff /* Enable IRQs (don't yet use FIQs). */
27291e542a4Sjoff enable_interrupts(I32_bit);
27391e542a4Sjoff }
27491e542a4Sjoff
275*44c2d104Sjmcneill void
ep93xx_intr_evcnt_attach(void)276*44c2d104Sjmcneill ep93xx_intr_evcnt_attach(void)
277*44c2d104Sjmcneill {
278*44c2d104Sjmcneill struct intrq *iq;
279*44c2d104Sjmcneill int i;
280*44c2d104Sjmcneill
281*44c2d104Sjmcneill for (i = 0; i < NIRQ; i++) {
282*44c2d104Sjmcneill iq = &intrq[i];
283*44c2d104Sjmcneill evcnt_attach_dynamic(&iq->iq_ev, EVCNT_TYPE_INTR,
284*44c2d104Sjmcneill NULL, (i < VIC_NIRQ ? "vic1" : "vic2"),
285*44c2d104Sjmcneill iq->iq_name);
286*44c2d104Sjmcneill }
287*44c2d104Sjmcneill }
288*44c2d104Sjmcneill
28991e542a4Sjoff void *
ep93xx_intr_establish(int irq,int ipl,int (* ih_func)(void *),void * arg)29091e542a4Sjoff ep93xx_intr_establish(int irq, int ipl, int (*ih_func)(void *), void *arg)
29191e542a4Sjoff {
29291e542a4Sjoff struct intrq* iq;
29391e542a4Sjoff struct intrhand* ih;
29491e542a4Sjoff u_int oldirqstate;
29591e542a4Sjoff
29691e542a4Sjoff if (irq < 0 || irq > NIRQ)
29791e542a4Sjoff panic("ep93xx_intr_establish: IRQ %d out of range", irq);
29891e542a4Sjoff if (ipl < 0 || ipl > NIPL)
29991e542a4Sjoff panic("ep93xx_intr_establish: IPL %d out of range", ipl);
30091e542a4Sjoff
30138fdb085Sthorpej ih = kmem_alloc(sizeof(*ih), KM_SLEEP);
30291e542a4Sjoff ih->ih_func = ih_func;
30391e542a4Sjoff ih->ih_arg = arg;
30491e542a4Sjoff ih->ih_irq = irq;
30591e542a4Sjoff ih->ih_ipl = ipl;
30691e542a4Sjoff
30791e542a4Sjoff iq = &intrq[irq];
30891e542a4Sjoff
30991e542a4Sjoff oldirqstate = disable_interrupts(I32_bit);
31091e542a4Sjoff TAILQ_INSERT_TAIL(&iq->iq_list, ih, ih_list);
31191e542a4Sjoff ep93xx_intr_calculate_masks();
31291e542a4Sjoff restore_interrupts(oldirqstate);
31391e542a4Sjoff
31491e542a4Sjoff return (ih);
31591e542a4Sjoff }
31691e542a4Sjoff
31791e542a4Sjoff void
ep93xx_intr_disestablish(void * cookie)31891e542a4Sjoff ep93xx_intr_disestablish(void *cookie)
31991e542a4Sjoff {
32091e542a4Sjoff struct intrhand* ih = cookie;
32191e542a4Sjoff struct intrq* iq = &intrq[ih->ih_irq];
32291e542a4Sjoff u_int oldirqstate;
32391e542a4Sjoff
32491e542a4Sjoff oldirqstate = disable_interrupts(I32_bit);
32591e542a4Sjoff TAILQ_REMOVE(&iq->iq_list, ih, ih_list);
32691e542a4Sjoff ep93xx_intr_calculate_masks();
32791e542a4Sjoff restore_interrupts(oldirqstate);
32838fdb085Sthorpej
32938fdb085Sthorpej kmem_free(ih, sizeof(*ih));
33091e542a4Sjoff }
33191e542a4Sjoff
33291e542a4Sjoff void
ep93xx_intr_dispatch(struct trapframe * frame)3331c15ddd3Sskrll ep93xx_intr_dispatch(struct trapframe *frame)
33491e542a4Sjoff {
33591e542a4Sjoff struct intrq* iq;
33691e542a4Sjoff struct intrhand* ih;
33791e542a4Sjoff u_int oldirqstate;
33891e542a4Sjoff int pcpl;
33908a4aba7Sskrll uint32_t vic1_hwpend;
34008a4aba7Sskrll uint32_t vic2_hwpend;
34191e542a4Sjoff int irq;
34291e542a4Sjoff
343825088edSmatt pcpl = curcpl();
34491e542a4Sjoff
34591e542a4Sjoff vic1_hwpend = VIC1REG(EP93XX_VIC_IRQStatus);
34691e542a4Sjoff vic2_hwpend = VIC2REG(EP93XX_VIC_IRQStatus);
34791e542a4Sjoff
34891e542a4Sjoff hardware_spl_level = pcpl;
34991e542a4Sjoff ep93xx_set_intrmask(vic1_imask[pcpl] | vic1_hwpend,
35091e542a4Sjoff vic2_imask[pcpl] | vic2_hwpend);
35191e542a4Sjoff
35291e542a4Sjoff vic1_hwpend &= ~vic1_imask[pcpl];
35391e542a4Sjoff vic2_hwpend &= ~vic2_imask[pcpl];
35491e542a4Sjoff
3559c1013d6Sjoff if (vic1_hwpend) {
35691e542a4Sjoff irq = ffs(vic1_hwpend) - 1;
35791e542a4Sjoff
35891e542a4Sjoff iq = &intrq[irq];
35991e542a4Sjoff iq->iq_ev.ev_count++;
3606a66466fSmatt curcpu()->ci_data.cpu_nintr++;
361825088edSmatt TAILQ_FOREACH(ih, &iq->iq_list, ih_list) {
362825088edSmatt set_curcpl(ih->ih_ipl);
36391e542a4Sjoff oldirqstate = enable_interrupts(I32_bit);
36491e542a4Sjoff (void) (*ih->ih_func)(ih->ih_arg ? ih->ih_arg : frame);
36591e542a4Sjoff restore_interrupts(oldirqstate);
36691e542a4Sjoff }
3679c1013d6Sjoff } else if (vic2_hwpend) {
36891e542a4Sjoff irq = ffs(vic2_hwpend) - 1;
36991e542a4Sjoff
37091e542a4Sjoff iq = &intrq[irq + VIC_NIRQ];
37191e542a4Sjoff iq->iq_ev.ev_count++;
3726a66466fSmatt curcpu()->ci_data.cpu_nintr++;
373825088edSmatt TAILQ_FOREACH(ih, &iq->iq_list, ih_list) {
374825088edSmatt set_curcpl(ih->ih_ipl);
37591e542a4Sjoff oldirqstate = enable_interrupts(I32_bit);
37691e542a4Sjoff (void) (*ih->ih_func)(ih->ih_arg ? ih->ih_arg : frame);
37791e542a4Sjoff restore_interrupts(oldirqstate);
37891e542a4Sjoff }
37991e542a4Sjoff }
38091e542a4Sjoff
381825088edSmatt set_curcpl(pcpl);
38291e542a4Sjoff hardware_spl_level = pcpl;
38391e542a4Sjoff ep93xx_set_intrmask(vic1_imask[pcpl], vic2_imask[pcpl]);
38491e542a4Sjoff
3850c0de807Smatt #ifdef __HAVE_FAST_SOFTINTS
386825088edSmatt cpu_dosoftints();
3870c0de807Smatt #endif
38891e542a4Sjoff }
389