Path: blob/main/sys/arm/broadcom/bcm2835/bcm2835_intr.c
39566 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2012 Damjan Marion <[email protected]>4* All rights reserved.5*6* Based on OMAP3 INTC code by Ben Gray7*8* Redistribution and use in source and binary forms, with or without9* modification, are permitted provided that the following conditions10* are met:11* 1. Redistributions of source code must retain the above copyright12* notice, this list of conditions and the following disclaimer.13* 2. Redistributions in binary form must reproduce the above copyright14* notice, this list of conditions and the following disclaimer in the15* documentation and/or other materials provided with the distribution.16*17* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND18* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE19* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE20* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE21* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL22* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS23* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)24* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT25* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY26* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF27* SUCH DAMAGE.28*/2930#include <sys/cdefs.h>31#include "opt_platform.h"3233#include <sys/param.h>34#include <sys/systm.h>35#include <sys/bus.h>36#include <sys/kernel.h>37#include <sys/ktr.h>38#include <sys/module.h>39#include <sys/proc.h>40#include <sys/rman.h>41#include <machine/bus.h>42#include <machine/intr.h>4344#include <dev/ofw/openfirm.h>45#include <dev/ofw/ofw_bus.h>46#include <dev/ofw/ofw_bus_subr.h>4748#include "pic_if.h"4950#define INTC_PENDING_BASIC 0x0051#define INTC_PENDING_BANK1 0x0452#define INTC_PENDING_BANK2 0x0853#define INTC_FIQ_CONTROL 0x0C54#define INTC_ENABLE_BANK1 0x1055#define INTC_ENABLE_BANK2 0x1456#define INTC_ENABLE_BASIC 0x1857#define INTC_DISABLE_BANK1 0x1C58#define INTC_DISABLE_BANK2 0x2059#define INTC_DISABLE_BASIC 0x246061#define INTC_PENDING_BASIC_ARM 0x0000FF62#define INTC_PENDING_BASIC_GPU1_PEND 0x00010063#define INTC_PENDING_BASIC_GPU2_PEND 0x00020064#define INTC_PENDING_BASIC_GPU1_7 0x00040065#define INTC_PENDING_BASIC_GPU1_9 0x00080066#define INTC_PENDING_BASIC_GPU1_10 0x00100067#define INTC_PENDING_BASIC_GPU1_18 0x00200068#define INTC_PENDING_BASIC_GPU1_19 0x00400069#define INTC_PENDING_BASIC_GPU2_21 0x00800070#define INTC_PENDING_BASIC_GPU2_22 0x01000071#define INTC_PENDING_BASIC_GPU2_23 0x02000072#define INTC_PENDING_BASIC_GPU2_24 0x04000073#define INTC_PENDING_BASIC_GPU2_25 0x08000074#define INTC_PENDING_BASIC_GPU2_30 0x10000075#define INTC_PENDING_BASIC_MASK 0x1FFFFF7677#define INTC_PENDING_BASIC_GPU1_MASK (INTC_PENDING_BASIC_GPU1_7 | \78INTC_PENDING_BASIC_GPU1_9 | \79INTC_PENDING_BASIC_GPU1_10 | \80INTC_PENDING_BASIC_GPU1_18 | \81INTC_PENDING_BASIC_GPU1_19)8283#define INTC_PENDING_BASIC_GPU2_MASK (INTC_PENDING_BASIC_GPU2_21 | \84INTC_PENDING_BASIC_GPU2_22 | \85INTC_PENDING_BASIC_GPU2_23 | \86INTC_PENDING_BASIC_GPU2_24 | \87INTC_PENDING_BASIC_GPU2_25 | \88INTC_PENDING_BASIC_GPU2_30)8990#define INTC_PENDING_BANK1_MASK (~((1 << 7) | (1 << 9) | (1 << 10) | \91(1 << 18) | (1 << 19)))92#define INTC_PENDING_BANK2_MASK (~((1 << 21) | (1 << 22) | (1 << 23) | \93(1 << 24) | (1 << 25) | (1 << 30)))9495#define BANK1_START 896#define BANK1_END (BANK1_START + 32 - 1)97#define BANK2_START (BANK1_START + 32)98#define BANK2_END (BANK2_START + 32 - 1)99100#define IS_IRQ_BASIC(n) (((n) >= 0) && ((n) < BANK1_START))101#define IS_IRQ_BANK1(n) (((n) >= BANK1_START) && ((n) <= BANK1_END))102#define IS_IRQ_BANK2(n) (((n) >= BANK2_START) && ((n) <= BANK2_END))103#define IRQ_BANK1(n) ((n) - BANK1_START)104#define IRQ_BANK2(n) ((n) - BANK2_START)105106#ifdef DEBUG107#define dprintf(fmt, args...) printf(fmt, ##args)108#else109#define dprintf(fmt, args...)110#endif111112#define BCM_INTC_NIRQS 72 /* 8 + 32 + 32 */113114struct bcm_intc_irqsrc {115struct intr_irqsrc bii_isrc;116u_int bii_irq;117uint16_t bii_disable_reg;118uint16_t bii_enable_reg;119uint32_t bii_mask;120};121122struct bcm_intc_softc {123device_t sc_dev;124struct resource * intc_res;125bus_space_tag_t intc_bst;126bus_space_handle_t intc_bsh;127struct resource * intc_irq_res;128void * intc_irq_hdl;129struct bcm_intc_irqsrc intc_isrcs[BCM_INTC_NIRQS];130};131132static struct ofw_compat_data compat_data[] = {133{"broadcom,bcm2835-armctrl-ic", 1},134{"brcm,bcm2835-armctrl-ic", 1},135{"brcm,bcm2836-armctrl-ic", 1},136{NULL, 0}137};138139static struct bcm_intc_softc *bcm_intc_sc = NULL;140141#define intc_read_4(_sc, reg) \142bus_space_read_4((_sc)->intc_bst, (_sc)->intc_bsh, (reg))143#define intc_write_4(_sc, reg, val) \144bus_space_write_4((_sc)->intc_bst, (_sc)->intc_bsh, (reg), (val))145146static inline void147bcm_intc_isrc_mask(struct bcm_intc_softc *sc, struct bcm_intc_irqsrc *bii)148{149150intc_write_4(sc, bii->bii_disable_reg, bii->bii_mask);151}152153static inline void154bcm_intc_isrc_unmask(struct bcm_intc_softc *sc, struct bcm_intc_irqsrc *bii)155{156157intc_write_4(sc, bii->bii_enable_reg, bii->bii_mask);158}159160static inline int161bcm2835_intc_active_intr(struct bcm_intc_softc *sc)162{163uint32_t pending, pending_gpu;164165pending = intc_read_4(sc, INTC_PENDING_BASIC) & INTC_PENDING_BASIC_MASK;166if (pending == 0)167return (-1);168if (pending & INTC_PENDING_BASIC_ARM)169return (ffs(pending) - 1);170if (pending & INTC_PENDING_BASIC_GPU1_MASK) {171if (pending & INTC_PENDING_BASIC_GPU1_7)172return (BANK1_START + 7);173if (pending & INTC_PENDING_BASIC_GPU1_9)174return (BANK1_START + 9);175if (pending & INTC_PENDING_BASIC_GPU1_10)176return (BANK1_START + 10);177if (pending & INTC_PENDING_BASIC_GPU1_18)178return (BANK1_START + 18);179if (pending & INTC_PENDING_BASIC_GPU1_19)180return (BANK1_START + 19);181}182if (pending & INTC_PENDING_BASIC_GPU2_MASK) {183if (pending & INTC_PENDING_BASIC_GPU2_21)184return (BANK2_START + 21);185if (pending & INTC_PENDING_BASIC_GPU2_22)186return (BANK2_START + 22);187if (pending & INTC_PENDING_BASIC_GPU2_23)188return (BANK2_START + 23);189if (pending & INTC_PENDING_BASIC_GPU2_24)190return (BANK2_START + 24);191if (pending & INTC_PENDING_BASIC_GPU2_25)192return (BANK2_START + 25);193if (pending & INTC_PENDING_BASIC_GPU2_30)194return (BANK2_START + 30);195}196if (pending & INTC_PENDING_BASIC_GPU1_PEND) {197pending_gpu = intc_read_4(sc, INTC_PENDING_BANK1);198pending_gpu &= INTC_PENDING_BANK1_MASK;199if (pending_gpu != 0)200return (BANK1_START + ffs(pending_gpu) - 1);201}202if (pending & INTC_PENDING_BASIC_GPU2_PEND) {203pending_gpu = intc_read_4(sc, INTC_PENDING_BANK2);204pending_gpu &= INTC_PENDING_BANK2_MASK;205if (pending_gpu != 0)206return (BANK2_START + ffs(pending_gpu) - 1);207}208return (-1); /* It shouldn't end here, but it's hardware. */209}210211static int212bcm2835_intc_intr(void *arg)213{214int irq, num;215struct bcm_intc_softc *sc = arg;216217for (num = 0; ; num++) {218irq = bcm2835_intc_active_intr(sc);219if (irq == -1)220break;221if (intr_isrc_dispatch(&sc->intc_isrcs[irq].bii_isrc,222curthread->td_intr_frame) != 0) {223bcm_intc_isrc_mask(sc, &sc->intc_isrcs[irq]);224device_printf(sc->sc_dev, "Stray irq %u disabled\n",225irq);226}227arm_irq_memory_barrier(0); /* XXX */228}229if (num == 0 && bootverbose)230device_printf(sc->sc_dev, "Spurious interrupt detected\n");231232return (FILTER_HANDLED);233}234235static void236bcm_intc_enable_intr(device_t dev, struct intr_irqsrc *isrc)237{238struct bcm_intc_irqsrc *bii = (struct bcm_intc_irqsrc *)isrc;239240arm_irq_memory_barrier(bii->bii_irq);241bcm_intc_isrc_unmask(device_get_softc(dev), bii);242}243244static void245bcm_intc_disable_intr(device_t dev, struct intr_irqsrc *isrc)246{247248bcm_intc_isrc_mask(device_get_softc(dev),249(struct bcm_intc_irqsrc *)isrc);250}251252static int253bcm_intc_map_intr(device_t dev, struct intr_map_data *data,254struct intr_irqsrc **isrcp)255{256u_int irq;257struct intr_map_data_fdt *daf;258struct bcm_intc_softc *sc;259bool valid;260261if (data->type != INTR_MAP_DATA_FDT)262return (ENOTSUP);263264daf = (struct intr_map_data_fdt *)data;265if (daf->ncells == 1)266irq = daf->cells[0];267else if (daf->ncells == 2) {268valid = true;269switch (daf->cells[0]) {270case 0:271irq = daf->cells[1];272if (irq >= BANK1_START)273valid = false;274break;275case 1:276irq = daf->cells[1] + BANK1_START;277if (irq > BANK1_END)278valid = false;279break;280case 2:281irq = daf->cells[1] + BANK2_START;282if (irq > BANK2_END)283valid = false;284break;285default:286valid = false;287break;288}289290if (!valid) {291device_printf(dev,292"invalid IRQ config: bank=%d, irq=%d\n",293daf->cells[0], daf->cells[1]);294return (EINVAL);295}296}297else298return (EINVAL);299300if (irq >= BCM_INTC_NIRQS)301return (EINVAL);302303sc = device_get_softc(dev);304*isrcp = &sc->intc_isrcs[irq].bii_isrc;305return (0);306}307308static void309bcm_intc_pre_ithread(device_t dev, struct intr_irqsrc *isrc)310{311312bcm_intc_disable_intr(dev, isrc);313}314315static void316bcm_intc_post_ithread(device_t dev, struct intr_irqsrc *isrc)317{318319bcm_intc_enable_intr(dev, isrc);320}321322static void323bcm_intc_post_filter(device_t dev, struct intr_irqsrc *isrc)324{325}326327static int328bcm_intc_pic_register(struct bcm_intc_softc *sc, intptr_t xref)329{330struct bcm_intc_irqsrc *bii;331int error;332uint32_t irq;333const char *name;334335name = device_get_nameunit(sc->sc_dev);336for (irq = 0; irq < BCM_INTC_NIRQS; irq++) {337bii = &sc->intc_isrcs[irq];338bii->bii_irq = irq;339if (IS_IRQ_BASIC(irq)) {340bii->bii_disable_reg = INTC_DISABLE_BASIC;341bii->bii_enable_reg = INTC_ENABLE_BASIC;342bii->bii_mask = 1 << irq;343} else if (IS_IRQ_BANK1(irq)) {344bii->bii_disable_reg = INTC_DISABLE_BANK1;345bii->bii_enable_reg = INTC_ENABLE_BANK1;346bii->bii_mask = 1 << IRQ_BANK1(irq);347} else if (IS_IRQ_BANK2(irq)) {348bii->bii_disable_reg = INTC_DISABLE_BANK2;349bii->bii_enable_reg = INTC_ENABLE_BANK2;350bii->bii_mask = 1 << IRQ_BANK2(irq);351} else352return (ENXIO);353354error = intr_isrc_register(&bii->bii_isrc, sc->sc_dev, 0,355"%s,%u", name, irq);356if (error != 0)357return (error);358}359if (intr_pic_register(sc->sc_dev, xref) == NULL)360return (ENXIO);361362return (0);363}364365static int366bcm_intc_probe(device_t dev)367{368369if (!ofw_bus_status_okay(dev))370return (ENXIO);371372if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)373return (ENXIO);374375device_set_desc(dev, "BCM2835 Interrupt Controller");376return (BUS_PROBE_DEFAULT);377}378379static int380bcm_intc_attach(device_t dev)381{382struct bcm_intc_softc *sc = device_get_softc(dev);383int rid = 0;384intptr_t xref;385sc->sc_dev = dev;386387if (bcm_intc_sc)388return (ENXIO);389390sc->intc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);391if (sc->intc_res == NULL) {392device_printf(dev, "could not allocate memory resource\n");393return (ENXIO);394}395396xref = OF_xref_from_node(ofw_bus_get_node(dev));397if (bcm_intc_pic_register(sc, xref) != 0) {398bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->intc_res);399device_printf(dev, "could not register PIC\n");400return (ENXIO);401}402403rid = 0;404sc->intc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,405RF_ACTIVE);406if (sc->intc_irq_res == NULL) {407if (intr_pic_claim_root(dev, xref, bcm2835_intc_intr, sc, INTR_ROOT_IRQ)408!= 0) {409/* XXX clean up */410device_printf(dev, "could not set PIC as a root\n");411return (ENXIO);412}413} else {414if (bus_setup_intr(dev, sc->intc_irq_res, INTR_TYPE_CLK,415bcm2835_intc_intr, NULL, sc, &sc->intc_irq_hdl)) {416/* XXX clean up */417device_printf(dev, "could not setup irq handler\n");418return (ENXIO);419}420}421sc->intc_bst = rman_get_bustag(sc->intc_res);422sc->intc_bsh = rman_get_bushandle(sc->intc_res);423424bcm_intc_sc = sc;425426return (0);427}428429static device_method_t bcm_intc_methods[] = {430DEVMETHOD(device_probe, bcm_intc_probe),431DEVMETHOD(device_attach, bcm_intc_attach),432433DEVMETHOD(pic_disable_intr, bcm_intc_disable_intr),434DEVMETHOD(pic_enable_intr, bcm_intc_enable_intr),435DEVMETHOD(pic_map_intr, bcm_intc_map_intr),436DEVMETHOD(pic_post_filter, bcm_intc_post_filter),437DEVMETHOD(pic_post_ithread, bcm_intc_post_ithread),438DEVMETHOD(pic_pre_ithread, bcm_intc_pre_ithread),439{ 0, 0 }440};441442static driver_t bcm_intc_driver = {443"intc",444bcm_intc_methods,445sizeof(struct bcm_intc_softc),446};447448EARLY_DRIVER_MODULE(intc, simplebus, bcm_intc_driver, 0, 0,449BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);450451452