diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2017-01-23 08:05:48 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2017-01-23 08:05:48 +0000 |
commit | 1b85daa0dc93fda98db66bc76ebe220b2736cd3b (patch) | |
tree | 5b9bbf77e0a9bf9aa878f9226fabc170eaa2d71c /sys/arch | |
parent | 69b2e5b3bd34dd1e98f2911b5aad5d837a3b5865 (diff) |
Add drivers for the Raspberry Pi 3.
Diffstat (limited to 'sys/arch')
-rw-r--r-- | sys/arch/arm64/conf/GENERIC | 7 | ||||
-rw-r--r-- | sys/arch/arm64/conf/files.arm64 | 10 | ||||
-rw-r--r-- | sys/arch/arm64/dev/bcm2835_dwctwo.c | 133 | ||||
-rw-r--r-- | sys/arch/arm64/dev/bcm2836_intr.c | 558 |
4 files changed, 706 insertions, 2 deletions
diff --git a/sys/arch/arm64/conf/GENERIC b/sys/arch/arm64/conf/GENERIC index 897f25b81a1..1594251c1cb 100644 --- a/sys/arch/arm64/conf/GENERIC +++ b/sys/arch/arm64/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.3 2017/01/23 00:17:25 patrick Exp $ +# $OpenBSD: GENERIC,v 1.4 2017/01/23 08:05:47 kettenis Exp $ # # GENERIC machine description file # @@ -57,6 +57,11 @@ vioblk* at virtio? viomb* at virtio? viornd* at virtio? +# Raspberry Pi 3 +bcmintc* at fdt? +dwctwo* at fdt? +usb* at dwctwo? + # Sunxi SoCs sxipio* at fdt? early 1 # GPIO pins for leds & PHYs gpio* at sxipio? diff --git a/sys/arch/arm64/conf/files.arm64 b/sys/arch/arm64/conf/files.arm64 index 1a35f96892f..ace2133f4d2 100644 --- a/sys/arch/arm64/conf/files.arm64 +++ b/sys/arch/arm64/conf/files.arm64 @@ -1,4 +1,4 @@ -# $OpenBSD: files.arm64,v 1.4 2017/01/21 10:58:15 reyk Exp $ +# $OpenBSD: files.arm64,v 1.5 2017/01/23 08:05:47 kettenis Exp $ maxpartitions 16 maxusers 2 8 64 @@ -119,5 +119,13 @@ device agtimer attach agtimer at fdt file arch/arm64/dev/agtimer.c agtimer +device bcmintc +attach bcmintc at fdt +file arch/arm64/dev/bcm2836_intr.c bcmintc + +include "dev/usb/dwc2/files.dwc2" +attach dwctwo at fdt with bcmdwctwo +file arch/arm64/dev/bcm2835_dwctwo.c bcmdwctwo needs-flag + attach virtio at fdt with virtio_mmio file arch/arm64/dev/virtio_mmio.c virtio_mmio diff --git a/sys/arch/arm64/dev/bcm2835_dwctwo.c b/sys/arch/arm64/dev/bcm2835_dwctwo.c new file mode 100644 index 00000000000..7305ac2745a --- /dev/null +++ b/sys/arch/arm64/dev/bcm2835_dwctwo.c @@ -0,0 +1,133 @@ +/* $OpenBSD: bcm2835_dwctwo.c,v 1.1 2017/01/23 08:05:47 kettenis Exp $ */ +/* + * Copyright (c) 2015 Masao Uebayashi <uebayasi@tombiinc.com> + * + * Permission to use, copy, modify, and/or 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. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/malloc.h> +#include <sys/pool.h> +#include <sys/kthread.h> + +#include <machine/intr.h> +#include <machine/bus.h> +#include <machine/fdt.h> + +#include <dev/ofw/openfirm.h> +#include <dev/ofw/fdt.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdivar.h> +#include <dev/usb/usb_mem.h> +#include <dev/usb/usb_quirks.h> + +#include <dev/usb/dwc2/dwc2var.h> +#include <dev/usb/dwc2/dwc2.h> +#include <dev/usb/dwc2/dwc2_core.h> + +struct bcm_dwctwo_softc { + struct dwc2_softc sc_dwc2; + void *sc_ih; +}; + +int bcm_dwctwo_match(struct device *, void *, void *); +void bcm_dwctwo_attach(struct device *, struct device *, void *); +void bcm_dwctwo_deferred(void *); + +const struct cfattach bcmdwctwo_ca = { + sizeof(struct bcm_dwctwo_softc), bcm_dwctwo_match, bcm_dwctwo_attach, +}; + +struct cfdriver dwctwo_cd = { + NULL, "dwctwo", DV_DULL +}; + +static struct dwc2_core_params bcm_dwctwo_params = { + .otg_cap = 0, /* HNP/SRP capable */ + .otg_ver = 0, /* 1.3 */ + .dma_enable = 1, + .dma_desc_enable = 0, + .speed = 0, /* High Speed */ + .enable_dynamic_fifo = 1, + .en_multiple_tx_fifo = 1, + .host_rx_fifo_size = 774, /* 774 DWORDs */ + .host_nperio_tx_fifo_size = 256, /* 256 DWORDs */ + .host_perio_tx_fifo_size = 512, /* 512 DWORDs */ + .max_transfer_size = 65535, + .max_packet_count = 511, + .host_channels = 8, + .phy_type = 1, /* UTMI */ + .phy_utmi_width = 8, /* 8 bits */ + .phy_ulpi_ddr = 0, /* Single */ + .phy_ulpi_ext_vbus = 0, + .i2c_enable = 0, + .ulpi_fs_ls = 0, + .host_support_fs_ls_low_power = 0, + .host_ls_low_power_phy_clk = 0, /* 48 MHz */ + .ts_dline = 0, + .reload_ctl = 0, + .ahbcfg = 0x10, + .uframe_sched = 1, +}; + +int +bcm_dwctwo_match(struct device *parent, void *match, void *aux) +{ + struct fdt_attach_args *faa = (struct fdt_attach_args *)aux; + + return (OF_is_compatible(faa->fa_node, "brcm,bcm2708-usb") || + OF_is_compatible(faa->fa_node, "brcm,bcm2835-usb")); +} + +void +bcm_dwctwo_attach(struct device *parent, struct device *self, void *aux) +{ + struct bcm_dwctwo_softc *sc = (struct bcm_dwctwo_softc *)self; + struct fdt_attach_args *faa = aux; + + printf("\n"); + + sc->sc_dwc2.sc_iot = faa->fa_iot; + sc->sc_dwc2.sc_bus.pipe_size = sizeof(struct usbd_pipe); + sc->sc_dwc2.sc_bus.dmatag = faa->fa_dmat; + sc->sc_dwc2.sc_params = &bcm_dwctwo_params; + + if (bus_space_map(faa->fa_iot, faa->fa_reg[0].addr, + faa->fa_reg[0].size, 0, &sc->sc_dwc2.sc_ioh)) + panic("%s: bus_space_map failed!", __func__); + + sc->sc_ih = arm_intr_establish_fdt_idx(faa->fa_node, 1, IPL_USB, + dwc2_intr, (void *)&sc->sc_dwc2, sc->sc_dwc2.sc_bus.bdev.dv_xname); + if (sc->sc_ih == NULL) + panic("%s: intr_establish failed!", __func__); + + kthread_create_deferred(bcm_dwctwo_deferred, sc); +} + +void +bcm_dwctwo_deferred(void *self) +{ + struct bcm_dwctwo_softc *sc = (struct bcm_dwctwo_softc *)self; + int rc; + + rc = dwc2_init(&sc->sc_dwc2); + if (rc != 0) + return; + + sc->sc_dwc2.sc_child = config_found(&sc->sc_dwc2.sc_bus.bdev, + &sc->sc_dwc2.sc_bus, usbctlprint); +} diff --git a/sys/arch/arm64/dev/bcm2836_intr.c b/sys/arch/arm64/dev/bcm2836_intr.c new file mode 100644 index 00000000000..16d6e1daa50 --- /dev/null +++ b/sys/arch/arm64/dev/bcm2836_intr.c @@ -0,0 +1,558 @@ +/* $OpenBSD: bcm2836_intr.c,v 1.1 2017/01/23 08:05:47 kettenis Exp $ */ +/* + * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org> + * Copyright (c) 2015 Patrick Wildt <patrick@blueri.se> + * + * 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. + */ + +#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 <machine/bus.h> +#include <machine/fdt.h> + +#include <dev/ofw/openfirm.h> +#include <dev/ofw/fdt.h> + +/* registers */ +#define INTC_PENDING_BANK0 0x00 +#define INTC_PENDING_BANK1 0x04 +#define INTC_PENDING_BANK2 0x08 +#define INTC_FIQ_CONTROL 0x0C +#define INTC_ENABLE_BANK1 0x10 +#define INTC_ENABLE_BANK2 0x14 +#define INTC_ENABLE_BANK0 0x18 +#define INTC_DISABLE_BANK1 0x1C +#define INTC_DISABLE_BANK2 0x20 +#define INTC_DISABLE_BANK0 0x24 + +/* arm local */ +#define ARM_LOCAL_CONTROL 0x00 +#define ARM_LOCAL_PRESCALER 0x08 +#define PRESCALER_19_2 0x80000000 /* 19.2 MHz */ +#define ARM_LOCAL_INT_TIMER(n) (0x40 + (n) * 4) +#define ARM_LOCAL_INT_MAILBOX(n) (0x50 + (n) * 4) +#define ARM_LOCAL_INT_PENDING(n) (0x60 + (n) * 4) +#define ARM_LOCAL_INT_PENDING_MASK 0x0f + +#define BANK0_START 64 +#define BANK0_END (BANK0_START + 32 - 1) +#define BANK1_START 0 +#define BANK1_END (BANK1_START + 32 - 1) +#define BANK2_START 32 +#define BANK2_END (BANK2_START + 32 - 1) +#define LOCAL_START 96 +#define LOCAL_END (LOCAL_START + 32 - 1) + +#define IS_IRQ_BANK0(n) (((n) >= BANK0_START) && ((n) <= BANK0_END)) +#define IS_IRQ_BANK1(n) (((n) >= BANK1_START) && ((n) <= BANK1_END)) +#define IS_IRQ_BANK2(n) (((n) >= BANK2_START) && ((n) <= BANK2_END)) +#define IS_IRQ_LOCAL(n) (((n) >= LOCAL_START) && ((n) <= LOCAL_END)) +#define IRQ_BANK0(n) ((n) - BANK0_START) +#define IRQ_BANK1(n) ((n) - BANK1_START) +#define IRQ_BANK2(n) ((n) - BANK2_START) +#define IRQ_LOCAL(n) ((n) - LOCAL_START) + +#define INTC_NIRQ 128 +#define INTC_NBANK 4 + +#define INTC_IRQ_TO_REG(i) (((i) >> 5) & 0x3) +#define INTC_IRQ_TO_REGi(i) ((i) & 0x1f) + +struct intrhand { + TAILQ_ENTRY(intrhand) ih_list; /* link on intrq list */ + int (*ih_fun)(void *); /* handler */ + void *ih_arg; /* arg for handler */ + int ih_ipl; /* IPL_* */ + int ih_irq; /* IRQ number */ + struct evcount ih_count; /* interrupt counter */ + char *ih_name; /* device name */ +}; + +struct intrsource { + TAILQ_HEAD(, intrhand) is_list; /* handler list */ + int is_irq; /* IRQ to mask while handling */ +}; + +struct bcm_intc_softc { + struct device sc_dev; + struct intrsource sc_bcm_intc_handler[INTC_NIRQ]; + uint32_t sc_bcm_intc_imask[INTC_NBANK][NIPL]; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + bus_space_handle_t sc_lioh; + struct interrupt_controller sc_intc; + struct interrupt_controller sc_l1_intc; +}; +struct bcm_intc_softc *bcm_intc; + +int bcm_intc_match(struct device *, void *, void *); +void bcm_intc_attach(struct device *, struct device *, void *); +void bcm_intc_splx(int new); +int bcm_intc_spllower(int new); +int bcm_intc_splraise(int new); +void bcm_intc_setipl(int new); +void bcm_intc_calc_mask(void); +void *bcm_intc_intr_establish(int, int, int (*)(void *), + void *, char *); +void *bcm_intc_intr_establish_fdt(void *, int *, int, int (*)(void *), + void *, char *); +void *l1_intc_intr_establish_fdt(void *, int *, int, int (*)(void *), + void *, char *); +void bcm_intc_intr_disestablish(void *); +void bcm_intc_irq_handler(void *); + +struct cfattach bcmintc_ca = { + sizeof (struct bcm_intc_softc), bcm_intc_match, bcm_intc_attach +}; + +struct cfdriver bcmintc_cd = { + NULL, "bcmintc", DV_DULL +}; + +int +bcm_intc_match(struct device *parent, void *cfdata, void *aux) +{ + struct fdt_attach_args *faa = aux; + + if (OF_is_compatible(faa->fa_node, "brcm,bcm2836-armctrl-ic")) + return 1; + + return 0; +} + +void +bcm_intc_attach(struct device *parent, struct device *self, void *aux) +{ + struct bcm_intc_softc *sc = (struct bcm_intc_softc *)self; + struct fdt_attach_args *faa = aux; + uint32_t reg[2]; + int node; + int i; + + if (faa->fa_nreg < 1) + return; + + bcm_intc = sc; + + sc->sc_iot = faa->fa_iot; + + if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, + faa->fa_reg[0].size, 0, &sc->sc_ioh)) + panic("%s: bus_space_map failed!", __func__); + + /* + * ARM control logic. + * + * XXX Should really be implemented as a separate interrupt + * controller, but for now it is easier to handle it together + * with its BCM2835 partner. + */ + node = OF_finddevice("/soc/local_intc"); + if (node == -1) + panic("%s: can't find ARM control logic\n", __func__); + + if (OF_getpropintarray(node, "reg", reg, sizeof(reg)) != sizeof(reg)) + panic("%s: can't map ARM control logic\n", __func__); + + if (bus_space_map(sc->sc_iot, reg[0], reg[1], 0, &sc->sc_lioh)) + panic("%s: bus_space_map failed!", __func__); + + printf("\n"); + + /* mask all interrupts */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_DISABLE_BANK0, + 0xffffffff); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_DISABLE_BANK1, + 0xffffffff); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_DISABLE_BANK2, + 0xffffffff); + + /* ARM control specific */ + bus_space_write_4(sc->sc_iot, sc->sc_lioh, ARM_LOCAL_CONTROL, 0); + bus_space_write_4(sc->sc_iot, sc->sc_lioh, ARM_LOCAL_PRESCALER, + PRESCALER_19_2); + for (i = 0; i < 4; i++) + bus_space_write_4(sc->sc_iot, sc->sc_lioh, + ARM_LOCAL_INT_TIMER(i), 0); + for (i = 0; i < 4; i++) + bus_space_write_4(sc->sc_iot, sc->sc_lioh, + ARM_LOCAL_INT_MAILBOX(i), 1); + + for (i = 0; i < INTC_NIRQ; i++) { + TAILQ_INIT(&sc->sc_bcm_intc_handler[i].is_list); + } + + bcm_intc_calc_mask(); + + /* insert self as interrupt handler */ + arm_set_intr_handler(bcm_intc_splraise, bcm_intc_spllower, + bcm_intc_splx, bcm_intc_setipl, + bcm_intc_intr_establish, bcm_intc_intr_disestablish, + bcm_intc_irq_handler); + + sc->sc_intc.ic_node = faa->fa_node; + sc->sc_intc.ic_cookie = sc; + sc->sc_intc.ic_establish = bcm_intc_intr_establish_fdt; + sc->sc_intc.ic_disestablish = bcm_intc_intr_disestablish; + arm_intr_register_fdt(&sc->sc_intc); + + sc->sc_l1_intc.ic_node = node; + sc->sc_l1_intc.ic_cookie = sc; + sc->sc_l1_intc.ic_establish = l1_intc_intr_establish_fdt; + sc->sc_l1_intc.ic_disestablish = bcm_intc_intr_disestablish; + arm_intr_register_fdt(&sc->sc_l1_intc); + + bcm_intc_setipl(IPL_HIGH); /* XXX ??? */ + enable_interrupts(); +} + +void +bcm_intc_intr_enable(int irq, int ipl) +{ + struct bcm_intc_softc *sc = bcm_intc; + + if (IS_IRQ_BANK0(irq)) + sc->sc_bcm_intc_imask[0][ipl] |= (1 << IRQ_BANK0(irq)); + else if (IS_IRQ_BANK1(irq)) + sc->sc_bcm_intc_imask[1][ipl] |= (1 << IRQ_BANK1(irq)); + else if (IS_IRQ_BANK2(irq)) + sc->sc_bcm_intc_imask[2][ipl] |= (1 << IRQ_BANK2(irq)); + else if (IS_IRQ_LOCAL(irq)) + sc->sc_bcm_intc_imask[3][ipl] |= (1 << IRQ_LOCAL(irq)); + else + printf("%s: invalid irq number: %d\n", __func__, irq); +} + +void +bcm_intc_intr_disable(int irq, int ipl) +{ + struct bcm_intc_softc *sc = bcm_intc; + + if (IS_IRQ_BANK0(irq)) + sc->sc_bcm_intc_imask[0][ipl] &= ~(1 << IRQ_BANK0(irq)); + else if (IS_IRQ_BANK1(irq)) + sc->sc_bcm_intc_imask[1][ipl] &= ~(1 << IRQ_BANK1(irq)); + else if (IS_IRQ_BANK2(irq)) + sc->sc_bcm_intc_imask[2][ipl] &= ~(1 << IRQ_BANK2(irq)); + else if (IS_IRQ_LOCAL(irq)) + sc->sc_bcm_intc_imask[3][ipl] &= ~(1 << IRQ_LOCAL(irq)); + else + printf("%s: invalid irq number: %d\n", __func__, irq); +} + +void +bcm_intc_calc_mask(void) +{ + struct cpu_info *ci = curcpu(); + struct bcm_intc_softc *sc = bcm_intc; + int irq; + struct intrhand *ih; + int i; + + for (irq = 0; irq < INTC_NIRQ; irq++) { + int max = IPL_NONE; + int min = IPL_HIGH; + TAILQ_FOREACH(ih, &sc->sc_bcm_intc_handler[irq].is_list, + ih_list) { + if (ih->ih_ipl > max) + max = ih->ih_ipl; + + if (ih->ih_ipl < min) + min = ih->ih_ipl; + } + + sc->sc_bcm_intc_handler[irq].is_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, INTC_IRQ_TO_REG(irq), + INTC_IRQ_TO_REGi(irq)); + } +#endif + /* Enable interrupts at lower levels, clear -> enable */ + for (i = 0; i < min; i++) + bcm_intc_intr_enable(irq, i); + for (; i <= IPL_HIGH; i++) + bcm_intc_intr_disable(irq, i); + } + arm_init_smask(); + bcm_intc_setipl(ci->ci_cpl); +} + +void +bcm_intc_splx(int new) +{ + struct cpu_info *ci = curcpu(); + + if (ci->ci_ipending & arm_smask[new]) + arm_do_pending_intr(new); + + bcm_intc_setipl(new); +} + +int +bcm_intc_spllower(int new) +{ + struct cpu_info *ci = curcpu(); + int old = ci->ci_cpl; + bcm_intc_splx(new); + return (old); +} + +int +bcm_intc_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; + + bcm_intc_setipl(new); + + return (old); +} + +void +bcm_intc_setipl(int new) +{ + struct cpu_info *ci = curcpu(); + struct bcm_intc_softc *sc = bcm_intc; + int i, psw; + + psw = disable_interrupts(); + ci->ci_cpl = new; + bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_DISABLE_BANK0, + 0xffffffff); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_DISABLE_BANK1, + 0xffffffff); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_DISABLE_BANK2, + 0xffffffff); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_ENABLE_BANK0, + sc->sc_bcm_intc_imask[0][new]); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_ENABLE_BANK1, + sc->sc_bcm_intc_imask[1][new]); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_ENABLE_BANK2, + sc->sc_bcm_intc_imask[2][new]); + /* XXX: SMP */ + for (i = 0; i < 4; i++) + bus_space_write_4(sc->sc_iot, sc->sc_lioh, + ARM_LOCAL_INT_TIMER(i), sc->sc_bcm_intc_imask[3][new]); + restore_interrupts(psw); +} + +int +bcm_intc_get_next_irq(int last_irq) +{ + struct bcm_intc_softc *sc = bcm_intc; + uint32_t pending; + int32_t irq = last_irq + 1; + + /* Sanity check */ + if (irq < 0) + irq = 0; + + /* We need to keep this order. */ + /* TODO: should we mask last_irq? */ + if (IS_IRQ_BANK1(irq)) { + pending = bus_space_read_4(sc->sc_iot, sc->sc_ioh, + INTC_PENDING_BANK1); + if (pending == 0) { + irq = BANK2_START; /* skip to next bank */ + } else do { + if (pending & (1 << IRQ_BANK1(irq))) + return irq; + irq++; + } while (IS_IRQ_BANK1(irq)); + } + if (IS_IRQ_BANK2(irq)) { + pending = bus_space_read_4(sc->sc_iot, sc->sc_ioh, + INTC_PENDING_BANK2); + if (pending == 0) { + irq = BANK0_START; /* skip to next bank */ + } else do { + if (pending & (1 << IRQ_BANK2(irq))) + return irq; + irq++; + } while (IS_IRQ_BANK2(irq)); + } + if (IS_IRQ_BANK0(irq)) { + pending = bus_space_read_4(sc->sc_iot, sc->sc_ioh, + INTC_PENDING_BANK0); + if (pending == 0) { + irq = LOCAL_START; /* skip to next bank */ + } else do { + if (pending & (1 << IRQ_BANK0(irq))) + return irq; + irq++; + } while (IS_IRQ_BANK0(irq)); + } + if (IS_IRQ_LOCAL(irq)) { + /* XXX: SMP */ + pending = bus_space_read_4(sc->sc_iot, sc->sc_lioh, + ARM_LOCAL_INT_PENDING(0)); + pending &= ARM_LOCAL_INT_PENDING_MASK; + if (pending != 0) do { + if (pending & (1 << IRQ_LOCAL(irq))) + return irq; + irq++; + } while (IS_IRQ_LOCAL(irq)); + } + return (-1); +} + +static void +bcm_intc_call_handler(int irq, void *frame) +{ + struct bcm_intc_softc *sc = bcm_intc; + struct intrhand *ih; + int pri, s; + void *arg; + +#ifdef DEBUG_INTC + if (irq != 99) + printf("irq %d fired\n", irq); + else { + static int cnt = 0; + if ((cnt++ % 100) == 0) { + printf("irq %d fired * _100\n", irq); + Debugger(); + } + } +#endif + + pri = sc->sc_bcm_intc_handler[irq].is_irq; + s = bcm_intc_splraise(pri); + TAILQ_FOREACH(ih, &sc->sc_bcm_intc_handler[irq].is_list, ih_list) { + if (ih->ih_arg != 0) + arg = ih->ih_arg; + else + arg = frame; + + if (ih->ih_fun(arg)) + ih->ih_count.ec_count++; + + } + + bcm_intc_splx(s); +} + +void +bcm_intc_irq_handler(void *frame) +{ + int irq = -1; + + while ((irq = bcm_intc_get_next_irq(irq)) != -1) + bcm_intc_call_handler(irq, frame); +} + +void * +bcm_intc_intr_establish_fdt(void *cookie, int *cell, int level, + int (*func)(void *), void *arg, char *name) +{ + struct bcm_intc_softc *sc = (struct bcm_intc_softc *)cookie; + int irq; + + irq = cell[1]; + if (cell[0] == 0) + irq += BANK0_START; + else if (cell[0] == 1) + irq += BANK1_START; + else if (cell[0] == 2) + irq += BANK2_START; + else if (cell[0] == 3) + irq += LOCAL_START; + else + panic("%s: bogus interrupt type", sc->sc_dev.dv_xname); + + return bcm_intc_intr_establish(irq, level, func, arg, name); +} + +void * +l1_intc_intr_establish_fdt(void *cookie, int *cell, int level, + int (*func)(void *), void *arg, char *name) +{ + int irq; + + irq = cell[0] + LOCAL_START; + return bcm_intc_intr_establish(irq, level, func, arg, name); +} + +void * +bcm_intc_intr_establish(int irqno, int level, int (*func)(void *), + void *arg, char *name) +{ + struct bcm_intc_softc *sc = bcm_intc; + struct intrhand *ih; + int psw; + + if (irqno < 0 || irqno >= INTC_NIRQ) + panic("bcm_intc_intr_establish: bogus irqnumber %d: %s", + irqno, name); + psw = disable_interrupts(); + + ih = malloc(sizeof *ih, M_DEVBUF, M_WAITOK); + ih->ih_fun = func; + ih->ih_arg = arg; + ih->ih_ipl = level; + ih->ih_irq = irqno; + ih->ih_name = name; + + TAILQ_INSERT_TAIL(&sc->sc_bcm_intc_handler[irqno].is_list, ih, ih_list); + + if (name != NULL) + evcount_attach(&ih->ih_count, name, &ih->ih_irq); + +#ifdef DEBUG_INTC + printf("%s irq %d level %d [%s]\n", __func__, irqno, level, + name); +#endif + bcm_intc_calc_mask(); + + restore_interrupts(psw); + return (ih); +} + +void +bcm_intc_intr_disestablish(void *cookie) +{ + struct bcm_intc_softc *sc = bcm_intc; + struct intrhand *ih = cookie; + int irqno = ih->ih_irq; + int psw; + + psw = disable_interrupts(); + TAILQ_REMOVE(&sc->sc_bcm_intc_handler[irqno].is_list, ih, ih_list); + if (ih->ih_name != NULL) + evcount_detach(&ih->ih_count); + free(ih, M_DEVBUF, 0); + restore_interrupts(psw); +} |