123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606 |
- /* $OpenBSD: ampintc.c,v 1.7 2015/07/15 21:09:40 jsg Exp $ */
- /*
- * Copyright (c) 2007,2009,2011 Dale Rahn <drahn@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
- /*
- * This driver implements the interrupt controller as specified in
- * DDI0407E_cortex_a9_mpcore_r2p0_trm with the
- * IHI0048A_gic_architecture_spec_v1_0 underlying specification
- */
- #include <sys/param.h>
- #include <sys/systm.h>
- #include <sys/queue.h>
- #include <sys/malloc.h>
- #include <sys/device.h>
- #include <sys/evcount.h>
- #include <arm/cpufunc.h>
- #include <machine/bus.h>
- #include <arm/cortex/cortex.h>
- /* offset from periphbase */
- #define ICP_ADDR 0x100
- #define ICP_SIZE 0x100
- #define ICD_ADDR 0x1000
- #define ICD_SIZE 0x1000
- #define ICD_A7_A15_ADDR 0x1000
- #define ICD_A7_A15_SIZE 0x1000
- #define ICP_A7_A15_ADDR 0x2000
- #define ICP_A7_A15_SIZE 0x1000
- /* registers */
- #define ICD_DCR 0x000
- #define ICD_DCR_ES 0x00000001
- #define ICD_DCR_ENS 0x00000002
- #define ICD_ICTR 0x004
- #define ICD_ICTR_LSPI_SH 11
- #define ICD_ICTR_LSPI_M 0x1f
- #define ICD_ICTR_CPU_SH 5
- #define ICD_ICTR_CPU_M 0x07
- #define ICD_ICTR_ITL_SH 0
- #define ICD_ICTR_ITL_M 0x1f
- #define ICD_IDIR 0x008
- #define ICD_DIR_PROD_SH 24
- #define ICD_DIR_PROD_M 0xff
- #define ICD_DIR_REV_SH 12
- #define ICD_DIR_REV_M 0xfff
- #define ICD_DIR_IMP_SH 0
- #define ICD_DIR_IMP_M 0xfff
- #define IRQ_TO_REG32(i) (((i) >> 5) & 0x7)
- #define IRQ_TO_REG32BIT(i) ((i) & 0x1f)
- #define IRQ_TO_REG4(i) (((i) >> 2) & 0x3f)
- #define IRQ_TO_REG4BIT(i) ((i) & 0x3)
- #define IRQ_TO_REG16(i) (((i) >> 4) & 0xf)
- #define IRQ_TO_REGBIT_S(i) 8
- #define IRQ_TO_REG4BIT_M(i) 8
- #define ICD_ISRn(i) (0x080 + (IRQ_TO_REG32(i) * 4))
- #define ICD_ISERn(i) (0x100 + (IRQ_TO_REG32(i) * 4))
- #define ICD_ICERn(i) (0x180 + (IRQ_TO_REG32(i) * 4))
- #define ICD_ISPRn(i) (0x200 + (IRQ_TO_REG32(i) * 4))
- #define ICD_ICPRn(i) (0x280 + (IRQ_TO_REG32(i) * 4))
- #define ICD_ABRn(i) (0x300 + (IRQ_TO_REG32(i) * 4))
- #define ICD_IPRn(i) (0x400 + (i))
- #define ICD_IPTRn(i) (0x800 + (i))
- #define ICD_ICRn(i) (0xC00 + (IRQ_TO_REG16(i) * 4))
- /*
- * what about (ppi|spi)_status
- */
- #define ICD_PPI 0xD00
- #define ICD_PPI_GTIMER (1 << 11)
- #define ICD_PPI_FIQ (1 << 12)
- #define ICD_PPI_PTIMER (1 << 13)
- #define ICD_PPI_PWDOG (1 << 14)
- #define ICD_PPI_IRQ (1 << 15)
- #define ICD_SPI_BASE 0xD04
- #define ICD_SPIn(i) (ICD_SPI_BASE + ((i) * 4))
- #define ICD_SGIR 0xF00
- #define ICD_PERIPH_ID_0 0xFD0
- #define ICD_PERIPH_ID_1 0xFD4
- #define ICD_PERIPH_ID_2 0xFD8
- #define ICD_PERIPH_ID_3 0xFDC
- #define ICD_PERIPH_ID_4 0xFE0
- #define ICD_PERIPH_ID_5 0xFE4
- #define ICD_PERIPH_ID_6 0xFE8
- #define ICD_PERIPH_ID_7 0xFEC
- #define ICD_COMP_ID_0 0xFEC
- #define ICD_COMP_ID_1 0xFEC
- #define ICD_COMP_ID_2 0xFEC
- #define ICD_COMP_ID_3 0xFEC
- #define ICD_SIZE 0x1000
- #define ICPICR 0x00
- #define ICPIPMR 0x04
- /* XXX - must left justify bits to 0 - 7 */
- #define ICMIPMR_SH 4
- #define ICPBPR 0x08
- #define ICPIAR 0x0C
- #define ICPIAR_IRQ_SH 0
- #define ICPIAR_IRQ_M 0x3ff
- #define ICPIAR_CPUID_SH 10
- #define ICPIAR_CPUID_M 0x7
- #define ICPIAR_NO_PENDING_IRQ ICPIAR_IRQ_M
- #define ICPEOIR 0x10
- #define ICPPRP 0x14
- #define ICPHPIR 0x18
- #define ICPIIR 0xFC
- #define ICP_SIZE 0x100
- /*
- * what about periph_id and component_id
- */
- #define AMPAMPINTC_SIZE 0x1000
- #define IRQ_ENABLE 1
- #define IRQ_DISABLE 0
- struct ampintc_softc {
- struct device sc_dev;
- struct intrq *sc_ampintc_handler;
- int sc_nintr;
- bus_space_tag_t sc_iot;
- bus_space_handle_t sc_d_ioh, sc_p_ioh;
- struct evcount sc_spur;
- };
- struct ampintc_softc *ampintc;
- struct intrhand {
- TAILQ_ENTRY(intrhand) ih_list; /* link on intrq list */
- int (*ih_func)(void *); /* handler */
- void *ih_arg; /* arg for handler */
- int ih_ipl; /* IPL_* */
- int ih_irq; /* IRQ number */
- struct evcount ih_count;
- char *ih_name;
- };
- struct intrq {
- TAILQ_HEAD(, intrhand) iq_list; /* handler list */
- int iq_irq; /* IRQ to mask while handling */
- int iq_levels; /* IPL_*'s this IRQ has */
- int iq_ist; /* share type */
- };
- int ampintc_match(struct device *, void *, void *);
- void ampintc_attach(struct device *, struct device *, void *);
- int ampintc_spllower(int);
- void ampintc_splx(int);
- int ampintc_splraise(int);
- void ampintc_setipl(int);
- void ampintc_calc_mask(void);
- void *ampintc_intr_establish(int, int, int (*)(void *), void *,
- char *);
- void *ampintc_intr_establish_ext(int, int, int (*)(void *), void *,
- char *);
- void ampintc_intr_disestablish(void *);
- void ampintc_irq_handler(void *);
- const char *ampintc_intr_string(void *);
- uint32_t ampintc_iack(void);
- void ampintc_eoi(uint32_t);
- void ampintc_set_priority(int, int);
- void ampintc_intr_enable(int);
- void ampintc_intr_disable(int);
- void ampintc_route(int, int , int);
- struct cfattach ampintc_ca = {
- sizeof (struct ampintc_softc), ampintc_match, ampintc_attach
- };
- struct cfdriver ampintc_cd = {
- NULL, "ampintc", DV_DULL
- };
- int
- ampintc_match(struct device *parent, void *cfdata, void *aux)
- {
- return (1);
- }
- paddr_t gic_dist_base, gic_cpu_base, gic_dist_size, gic_cpu_size;
- void
- ampintc_attach(struct device *parent, struct device *self, void *args)
- {
- struct ampintc_softc *sc = (struct ampintc_softc *)self;
- struct cortex_attach_args *ia = args;
- int i, nintr;
- bus_space_tag_t iot;
- bus_space_handle_t d_ioh, p_ioh;
- uint32_t icp, icpsize, icd, icdsize;
- ampintc = sc;
- arm_init_smask();
- iot = ia->ca_iot;
- icp = ia->ca_periphbase + ICP_ADDR;
- icpsize = ICP_SIZE;
- icd = ia->ca_periphbase + ICD_ADDR;
- icdsize = ICD_SIZE;
- if ((cputype & CPU_ID_CORTEX_A7_MASK) == CPU_ID_CORTEX_A7 ||
- (cputype & CPU_ID_CORTEX_A15_MASK) == CPU_ID_CORTEX_A15 ||
- (cputype & CPU_ID_CORTEX_A17_MASK) == CPU_ID_CORTEX_A17) {
- icp = ia->ca_periphbase + ICP_A7_A15_ADDR;
- icpsize = ICP_A7_A15_SIZE;
- icd = ia->ca_periphbase + ICD_A7_A15_ADDR;
- icdsize = ICD_A7_A15_SIZE;
- }
- /* exynos gic isn't at the expected offsets from periphbase */
- if (gic_cpu_base)
- icp = gic_cpu_base;
- if (gic_cpu_size)
- icpsize = gic_cpu_size;
- if (gic_dist_base)
- icd = gic_dist_base;
- if (gic_dist_size)
- icdsize = gic_dist_size;
- if (bus_space_map(iot, icp, icpsize, 0, &p_ioh))
- panic("ampintc_attach: ICP bus_space_map failed!");
- if (bus_space_map(iot, icd, icdsize, 0, &d_ioh))
- panic("ampintc_attach: ICD bus_space_map failed!");
- sc->sc_iot = iot;
- sc->sc_d_ioh = d_ioh;
- sc->sc_p_ioh = p_ioh;
- evcount_attach(&sc->sc_spur, "irq1023/spur", NULL);
- nintr = 32 * (bus_space_read_4(iot, d_ioh, ICD_ICTR) & ICD_ICTR_ITL_M);
- nintr += 32; /* ICD_ICTR + 1, irq 0-31 is SGI, 32+ is PPI */
- sc->sc_nintr = nintr;
- printf(" nirq %d\n", nintr);
- /* Disable all interrupts, clear all pending */
- for (i = 0; i < nintr/32; i++) {
- bus_space_write_4(iot, d_ioh, ICD_ICERn(i*32), ~0);
- bus_space_write_4(iot, d_ioh, ICD_ICPRn(i*32), ~0);
- }
- for (i = 0; i < nintr; i++) {
- /* lowest priority ?? */
- bus_space_write_1(iot, d_ioh, ICD_IPRn(i), 0xff);
- /* target no cpus */
- bus_space_write_1(iot, d_ioh, ICD_IPTRn(i), 0);
- }
- for (i = 2; i < nintr/16; i++) {
- /* irq 32 - N */
- bus_space_write_4(iot, d_ioh, ICD_ICRn(i*16), 0);
- }
- /* software reset of the part? */
- /* set protection bit (kernel only)? */
- /* XXX - check power saving bit */
- sc->sc_ampintc_handler = mallocarray(nintr,
- sizeof(*sc->sc_ampintc_handler), M_DEVBUF, M_ZERO | M_NOWAIT);
- for (i = 0; i < nintr; i++) {
- TAILQ_INIT(&sc->sc_ampintc_handler[i].iq_list);
- }
- ampintc_setipl(IPL_HIGH); /* XXX ??? */
- ampintc_calc_mask();
- /* insert self as interrupt handler */
- arm_set_intr_handler(ampintc_splraise, ampintc_spllower, ampintc_splx,
- ampintc_setipl, ampintc_intr_establish_ext,
- ampintc_intr_disestablish, ampintc_intr_string, ampintc_irq_handler);
- /* enable interrupts */
- bus_space_write_4(iot, d_ioh, ICD_DCR, 3);
- bus_space_write_4(iot, p_ioh, ICPICR, 1);
- enable_interrupts(I32_bit);
- }
- void
- ampintc_set_priority(int irq, int pri)
- {
- struct ampintc_softc *sc = ampintc;
- uint32_t prival;
- /*
- * We only use 16 (13 really) interrupt priorities,
- * and a CPU is only required to implement bit 4-7 of each field
- * so shift into the top bits.
- * also low values are higher priority thus IPL_HIGH - pri
- */
- prival = (IPL_HIGH - pri) << ICMIPMR_SH;
- bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPRn(irq), prival);
- }
- void
- ampintc_setipl(int new)
- {
- struct cpu_info *ci = curcpu();
- struct ampintc_softc *sc = ampintc;
- int psw;
- /* disable here is only to keep hardware in sync with ci->ci_cpl */
- psw = disable_interrupts(I32_bit);
- ci->ci_cpl = new;
- /* low values are higher priority thus IPL_HIGH - pri */
- bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPIPMR,
- (IPL_HIGH - new) << ICMIPMR_SH);
- restore_interrupts(psw);
- }
- void
- ampintc_intr_enable(int irq)
- {
- struct ampintc_softc *sc = ampintc;
- #ifdef DEBUG
- printf("enable irq %d register %x bitmask %08x\n",
- irq, ICD_ISERn(irq), 1 << IRQ_TO_REG32BIT(irq));
- #endif
- bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ISERn(irq),
- 1 << IRQ_TO_REG32BIT(irq));
- }
- void
- ampintc_intr_disable(int irq)
- {
- struct ampintc_softc *sc = ampintc;
- bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ICERn(irq),
- 1 << IRQ_TO_REG32BIT(irq));
- }
- void
- ampintc_calc_mask(void)
- {
- struct cpu_info *ci = curcpu();
- struct ampintc_softc *sc = ampintc;
- struct intrhand *ih;
- int irq;
- for (irq = 0; irq < sc->sc_nintr; irq++) {
- int max = IPL_NONE;
- int min = IPL_HIGH;
- TAILQ_FOREACH(ih, &sc->sc_ampintc_handler[irq].iq_list,
- ih_list) {
- if (ih->ih_ipl > max)
- max = ih->ih_ipl;
- if (ih->ih_ipl < min)
- min = ih->ih_ipl;
- }
- if (sc->sc_ampintc_handler[irq].iq_irq == max) {
- continue;
- }
- sc->sc_ampintc_handler[irq].iq_irq = max;
- if (max == IPL_NONE)
- min = IPL_NONE;
- #ifdef DEBUG_INTC
- if (min != IPL_NONE) {
- printf("irq %d to block at %d %d reg %d bit %d\n",
- irq, max, min, AMPINTC_IRQ_TO_REG(irq),
- AMPINTC_IRQ_TO_REGi(irq));
- }
- #endif
- /* Enable interrupts at lower levels, clear -> enable */
- /* Set interrupt priority/enable */
- if (min != IPL_NONE) {
- ampintc_set_priority(irq, min);
- ampintc_intr_enable(irq);
- ampintc_route(irq, IRQ_ENABLE, 0);
- } else {
- ampintc_intr_disable(irq);
- ampintc_route(irq, IRQ_DISABLE, 0);
- }
- }
- ampintc_setipl(ci->ci_cpl);
- }
- void
- ampintc_splx(int new)
- {
- struct cpu_info *ci = curcpu();
- if (ci->ci_ipending & arm_smask[new])
- arm_do_pending_intr(new);
- ampintc_setipl(new);
- }
- int
- ampintc_spllower(int new)
- {
- struct cpu_info *ci = curcpu();
- int old = ci->ci_cpl;
- ampintc_splx(new);
- return (old);
- }
- int
- ampintc_splraise(int new)
- {
- struct cpu_info *ci = curcpu();
- int old;
- old = ci->ci_cpl;
- /*
- * setipl must always be called because there is a race window
- * where the variable is updated before the mask is set
- * an interrupt occurs in that window without the mask always
- * being set, the hardware might not get updated on the next
- * splraise completely messing up spl protection.
- */
- if (old > new)
- new = old;
- ampintc_setipl(new);
- return (old);
- }
- uint32_t
- ampintc_iack(void)
- {
- uint32_t intid;
- struct ampintc_softc *sc = ampintc;
- intid = bus_space_read_4(sc->sc_iot, sc->sc_p_ioh, ICPIAR);
- return (intid);
- }
- void
- ampintc_eoi(uint32_t eoi)
- {
- struct ampintc_softc *sc = ampintc;
- bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPEOIR, eoi);
- }
- void
- ampintc_route(int irq, int enable, int cpu)
- {
- uint8_t val;
- struct ampintc_softc *sc = ampintc;
- val = bus_space_read_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPTRn(irq));
- if (enable == IRQ_ENABLE)
- val |= (1 << cpu);
- else
- val &= ~(1 << cpu);
- bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPTRn(irq), val);
- }
- void
- ampintc_irq_handler(void *frame)
- {
- struct ampintc_softc *sc = ampintc;
- struct intrhand *ih;
- void *arg;
- uint32_t iack_val;
- int irq, pri, s;
- iack_val = ampintc_iack();
- //#define DEBUG_INTC
- #ifdef DEBUG_INTC
- if (iack_val != 27)
- printf("irq %d fired\n", iack_val);
- else {
- static int cnt = 0;
- if ((cnt++ % 100) == 0) {
- printf("irq %d fired * _100\n", iack_val);
- Debugger();
- }
- }
- #endif
- if (iack_val == 1023) {
- sc->sc_spur.ec_count++;
- return;
- }
- irq = iack_val & ((1 << sc->sc_nintr) - 1);
- pri = sc->sc_ampintc_handler[irq].iq_irq;
- s = ampintc_splraise(pri);
- TAILQ_FOREACH(ih, &sc->sc_ampintc_handler[irq].iq_list, ih_list) {
- if (ih->ih_arg != 0)
- arg = ih->ih_arg;
- else
- arg = frame;
- if (ih->ih_func(arg))
- ih->ih_count.ec_count++;
- }
- ampintc_eoi(iack_val);
- ampintc_splx(s);
- }
- void *
- ampintc_intr_establish_ext(int irqno, int level, int (*func)(void *),
- void *arg, char *name)
- {
- return ampintc_intr_establish(irqno+32, level, func, arg, name);
- }
- void *
- ampintc_intr_establish(int irqno, int level, int (*func)(void *),
- void *arg, char *name)
- {
- struct ampintc_softc *sc = ampintc;
- struct intrhand *ih;
- int psw;
- if (irqno < 0 || irqno >= sc->sc_nintr)
- panic("ampintc_intr_establish: bogus irqnumber %d: %s",
- irqno, name);
- /* no point in sleeping unless someone can free memory. */
- ih = (struct intrhand *)malloc (sizeof *ih, M_DEVBUF,
- cold ? M_NOWAIT : M_WAITOK);
- if (ih == NULL)
- panic("intr_establish: can't malloc handler info");
- ih->ih_func = func;
- ih->ih_arg = arg;
- ih->ih_ipl = level;
- ih->ih_irq = irqno;
- ih->ih_name = name;
- psw = disable_interrupts(I32_bit);
- TAILQ_INSERT_TAIL(&sc->sc_ampintc_handler[irqno].iq_list, ih, ih_list);
- if (name != NULL)
- evcount_attach(&ih->ih_count, name, &ih->ih_irq);
- #ifdef DEBUG_INTC
- printf("ampintc_intr_establish irq %d level %d [%s]\n", irqno, level,
- name);
- #endif
- ampintc_calc_mask();
-
- restore_interrupts(psw);
- return (ih);
- }
- void
- ampintc_intr_disestablish(void *cookie)
- {
- #if 0
- int psw;
- struct intrhand *ih = cookie;
- int irqno = ih->ih_irq;
- psw = disable_interrupts(I32_bit);
- TAILQ_REMOVE(&sc->sc_ampintc_handler[irqno].iq_list, ih, ih_list);
- if (ih->ih_name != NULL)
- evcount_detach(&ih->ih_count);
- free(ih, M_DEVBUF, 0);
- restore_interrupts(psw);
- #endif
- }
- const char *
- ampintc_intr_string(void *cookie)
- {
- struct intrhand *ih = (struct intrhand *)cookie;
- static char irqstr[1 + sizeof("ampintc irq ") + 4];
- snprintf(irqstr, sizeof irqstr, "ampintc irq %d", ih->ih_irq);
- return irqstr;
- }
|