summaryrefslogtreecommitdiff
path: root/sys/arch
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2017-01-23 08:05:48 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2017-01-23 08:05:48 +0000
commit1b85daa0dc93fda98db66bc76ebe220b2736cd3b (patch)
tree5b9bbf77e0a9bf9aa878f9226fabc170eaa2d71c /sys/arch
parent69b2e5b3bd34dd1e98f2911b5aad5d837a3b5865 (diff)
Add drivers for the Raspberry Pi 3.
Diffstat (limited to 'sys/arch')
-rw-r--r--sys/arch/arm64/conf/GENERIC7
-rw-r--r--sys/arch/arm64/conf/files.arm6410
-rw-r--r--sys/arch/arm64/dev/bcm2835_dwctwo.c133
-rw-r--r--sys/arch/arm64/dev/bcm2836_intr.c558
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);
+}