--- /dev/null
+/* $OpenBSD: bcm2835_dwctwo.c,v 1.1 2016/08/07 17:46:36 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;
+ struct arm32_bus_dma_tag sc_dmat;
+ struct arm32_dma_range sc_dmarange[1];
+
+ 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;
+ extern int physmem;
+
+ printf("\n");
+
+ memcpy(&sc->sc_dmat, faa->fa_dmat, sizeof(sc->sc_dmat));
+ sc->sc_dmarange[0].dr_sysbase = 0;
+ sc->sc_dmarange[0].dr_busbase = 0xC0000000;
+ sc->sc_dmarange[0].dr_len = physmem * PAGE_SIZE;
+ sc->sc_dmat._ranges = sc->sc_dmarange;
+ sc->sc_dmat._nranges = 1;
+
+ 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 = &sc->sc_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);
+}
--- /dev/null
+/* $OpenBSD: bcm2835_muart.c,v 1.1 2016/08/07 17:46:36 kettenis Exp $ */
+/*
+ * Copyright (c) 2016 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/conf.h>
+#include <sys/device.h>
+#include <sys/tty.h>
+
+#include <machine/intr.h>
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#include <armv7/armv7/armv7_machdep.h>
+#include <arm/armv7/armv7var.h>
+
+#include <dev/ic/comvar.h>
+
+#include <dev/ofw/fdt.h>
+#include <dev/ofw/openfirm.h>
+
+int bcmmuart_match(struct device *, void *, void *);
+void bcmmuart_attach(struct device *, struct device *, void *);
+
+void bcmmuart_init_cons(void);
+
+struct cfdriver bcmmuart_cd = {
+ NULL, "bcmmuart", DV_DULL
+};
+
+struct cfattach bcmmuart_ca = {
+ sizeof (struct com_softc), bcmmuart_match, bcmmuart_attach,
+};
+
+int
+bcmmuart_match(struct device *parent, void *self, void *aux)
+{
+ struct fdt_attach_args *faa = aux;
+
+ if (OF_is_compatible(faa->fa_node, "brcm,bcm2835-aux-uart"))
+ return 1;
+
+ return 0;
+}
+
+void
+bcmmuart_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct com_softc *sc = (struct com_softc *)self;
+ struct fdt_attach_args *faa = aux;
+
+ if (faa->fa_nreg < 1)
+ return;
+
+ sc->sc_frequency = 500000000;
+ sc->sc_uarttype = COM_UART_16550;
+
+ if (faa->fa_reg[0].addr == comconsaddr + 0x3f000000) {
+ sc->sc_iobase = comconsaddr;
+ sc->sc_iot = comconsiot;
+ sc->sc_ioh = comconsioh;
+ } else {
+ sc->sc_iot = faa->fa_iot; /* XXX */
+ sc->sc_iobase = faa->fa_reg[0].addr;
+ if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
+ faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
+ printf("%s: bus_space_map failed\n", __func__);
+ return;
+ }
+ }
+
+ com_attach_subr(sc);
+
+ arm_intr_establish_fdt(faa->fa_node, IPL_TTY, comintr, sc,
+ sc->sc_dev.dv_xname);
+
+ extern struct cfdriver com_cd;
+ com_cd.cd_devs = bcmmuart_cd.cd_devs;
+ com_cd.cd_ndevs = bcmmuart_cd.cd_ndevs;
+}
+
+extern int comcnspeed;
+extern int comcnmode;
+
+void
+bcmmuart_init_cons(void)
+{
+ struct fdt_reg reg;
+ void *node;
+
+ if ((node = fdt_find_cons("brcm,bcm2835-aux-uart")) == NULL)
+ return;
+ if (fdt_get_reg(node, 0, ®))
+ return;
+
+ comcnattach(&armv7_a4x_bs_tag, reg.addr, comcnspeed, 500000000,
+ comcnmode);
+}
--- /dev/null
+/* $OpenBSD: bcm2836_intr.c,v 1.1 2016/08/07 17:46:36 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 <arm/cpufunc.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 *);
+const char *bcm_intc_intr_string(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;
+ 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 Local area, because fuck you.
+ *
+ * The original rPi with the BCM2835 does implement the same IC
+ * but with a different IRQ basis. On the BCM2836 there's an
+ * additional area to enable Timer/Mailbox interrupts, which
+ * is not yet exposed in the given DT.
+ */
+ extern struct bus_space armv7_bs_tag;
+ if (bus_space_map(&armv7_bs_tag, 0x40000000, 0x1000, 0,
+ &sc->sc_lioh))
+ panic("%s: bus_space_map failed!", __func__);
+
+ bus_space_write_4(sc->sc_iot, sc->sc_lioh, 0, 0);
+ bus_space_write_4(sc->sc_iot, sc->sc_lioh, 8, 0x80000000);
+
+ 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 local 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_intr_string,
+ 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);
+
+ node = OF_finddevice("/soc/local_intc");
+ if (node != -1) {
+ 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(PSR_I);
+}
+
+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(PSR_I);
+ 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(PSR_I);
+
+ 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(PSR_I);
+ 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);
+}
+
+const char *
+bcm_intc_intr_string(void *cookie)
+{
+ return "huh?";
+}