summaryrefslogtreecommitdiffstats
path: root/sys/mips/mediatek/mtk_intr_v2.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/mips/mediatek/mtk_intr_v2.c')
-rw-r--r--sys/mips/mediatek/mtk_intr_v2.c348
1 files changed, 348 insertions, 0 deletions
diff --git a/sys/mips/mediatek/mtk_intr_v2.c b/sys/mips/mediatek/mtk_intr_v2.c
new file mode 100644
index 0000000..5a9646e
--- /dev/null
+++ b/sys/mips/mediatek/mtk_intr_v2.c
@@ -0,0 +1,348 @@
+/*-
+ * Copyright (c) 2015 Stanislav Galabov
+ * Copyright (c) 2015 Alexander Kabaev
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/ktr.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/pcpu.h>
+#include <sys/proc.h>
+#include <sys/cpuset.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/smp.h>
+#include <sys/sched.h>
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/smp.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "pic_if.h"
+
+#define MTK_NIRQS 32
+
+#define MTK_IRQ0STAT 0x009c
+#define MTK_IRQ1STAT 0x00a0
+#define MTK_INTTYPE 0x0000
+#define MTK_INTRAW 0x00a4
+#define MTK_INTENA 0x0080
+#define MTK_INTDIS 0x0078
+
+static int mtk_pic_intr(void *);
+
+struct mtk_pic_irqsrc {
+ struct intr_irqsrc isrc;
+ u_int irq;
+};
+
+struct mtk_pic_softc {
+ device_t pic_dev;
+ void * pic_intrhand;
+ struct resource * pic_res[2];
+ struct mtk_pic_irqsrc pic_irqs[MTK_NIRQS];
+ struct mtx mutex;
+ uint32_t nirqs;
+};
+
+#define PIC_INTR_ISRC(sc, irq) (&(sc)->pic_irqs[(irq)].isrc)
+
+static struct resource_spec mtk_pic_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Registers */
+ { SYS_RES_IRQ, 0, RF_ACTIVE }, /* Parent interrupt 1 */
+// { SYS_RES_IRQ, 1, RF_ACTIVE }, /* Parent interrupt 2 */
+ { -1, 0 }
+};
+
+static struct ofw_compat_data compat_data[] = {
+ { "ralink,mt7628an-intc", 1 },
+ { NULL, 0 }
+};
+
+#define READ4(_sc, _reg) bus_read_4((_sc)->pic_res[0], _reg)
+#define WRITE4(_sc, _reg, _val) bus_write_4((_sc)->pic_res[0], _reg, _val)
+
+static int
+mtk_pic_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "MTK Interrupt Controller (v2)");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static inline void
+pic_irq_unmask(struct mtk_pic_softc *sc, u_int irq)
+{
+
+ WRITE4(sc, MTK_INTENA, (1u << (irq)));
+}
+
+static inline void
+pic_irq_mask(struct mtk_pic_softc *sc, u_int irq)
+{
+
+ WRITE4(sc, MTK_INTDIS, (1u << (irq)));
+}
+
+static inline intptr_t
+pic_xref(device_t dev)
+{
+ return (OF_xref_from_node(ofw_bus_get_node(dev)));
+}
+
+static int
+mtk_pic_register_isrcs(struct mtk_pic_softc *sc)
+{
+ int error;
+ uint32_t irq;
+ struct intr_irqsrc *isrc;
+ const char *name;
+
+ name = device_get_nameunit(sc->pic_dev);
+ for (irq = 0; irq < sc->nirqs; irq++) {
+ sc->pic_irqs[irq].irq = irq;
+ isrc = PIC_INTR_ISRC(sc, irq);
+ error = intr_isrc_register(isrc, sc->pic_dev, 0, "%s", name);
+ if (error != 0) {
+ /* XXX call intr_isrc_deregister */
+ device_printf(sc->pic_dev, "%s failed", __func__);
+ return (error);
+ }
+ }
+
+ return (0);
+}
+
+static int
+mtk_pic_attach(device_t dev)
+{
+ struct mtk_pic_softc *sc;
+ intptr_t xref = pic_xref(dev);
+
+ sc = device_get_softc(dev);
+
+ if (bus_alloc_resources(dev, mtk_pic_spec, sc->pic_res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ sc->pic_dev = dev;
+
+ /* Initialize mutex */
+ mtx_init(&sc->mutex, "PIC lock", "", MTX_SPIN);
+
+ /* Set the number of interrupts */
+ sc->nirqs = nitems(sc->pic_irqs);
+
+ /* Mask all interrupts */
+ WRITE4(sc, MTK_INTDIS, 0xFFFFFFFF);
+
+ /* But enable interrupt generation/masking */
+ WRITE4(sc, MTK_INTENA, 0x00000000);
+
+ /* Set all interrupts to type 0 */
+ WRITE4(sc, MTK_INTTYPE, 0xFFFFFFFF);
+
+ /* Register the interrupts */
+ if (mtk_pic_register_isrcs(sc) != 0) {
+ device_printf(dev, "could not register PIC ISRCs\n");
+ goto cleanup;
+ }
+
+ /*
+ * Now, when everything is initialized, it's right time to
+ * register interrupt controller to interrupt framefork.
+ */
+ if (intr_pic_register(dev, xref) != 0) {
+ device_printf(dev, "could not register PIC\n");
+ goto cleanup;
+ }
+
+ if (bus_setup_intr(dev, sc->pic_res[1], INTR_TYPE_CLK,
+ mtk_pic_intr, NULL, sc, &sc->pic_intrhand)) {
+ device_printf(dev, "could not setup irq handler\n");
+ intr_pic_deregister(dev, xref);
+ goto cleanup;
+ }
+ return (0);
+
+cleanup:
+ bus_release_resources(dev, mtk_pic_spec, sc->pic_res);
+ return(ENXIO);
+}
+
+static int
+mtk_pic_intr(void *arg)
+{
+ struct mtk_pic_softc *sc = arg;
+ struct thread *td;
+ uint32_t i, intr;
+
+ td = curthread;
+ /* Workaround: do not inflate intr nesting level */
+ td->td_intr_nesting_level--;
+
+#ifdef _notyet_
+ intr = READ4(sc, MTK_IRQ1STAT);
+ while ((i = fls(intr)) != 0) {
+ i--;
+ intr &= ~(1u << i);
+
+ if (intr_isrc_dispatch(PIC_INTR_ISRC(sc, i),
+ curthread->td_intr_frame) != 0) {
+ device_printf(sc->pic_dev,
+ "Stray interrupt %u detected\n", i);
+ pic_irq_mask(sc, i);
+ continue;
+ }
+ }
+
+ KASSERT(i == 0, ("all interrupts handled"));
+#endif
+
+ intr = READ4(sc, MTK_IRQ0STAT);
+
+ while ((i = fls(intr)) != 0) {
+ i--;
+ intr &= ~(1u << i);
+
+ if (intr_isrc_dispatch(PIC_INTR_ISRC(sc, i),
+ curthread->td_intr_frame) != 0) {
+ device_printf(sc->pic_dev,
+ "Stray interrupt %u detected\n", i);
+ pic_irq_mask(sc, i);
+ continue;
+ }
+ }
+
+ KASSERT(i == 0, ("all interrupts handled"));
+
+ td->td_intr_nesting_level++;
+
+ return (FILTER_HANDLED);
+}
+
+static int
+mtk_pic_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+#ifdef FDT
+ struct mtk_pic_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (data == NULL || data->type != INTR_MAP_DATA_FDT ||
+ data->fdt.ncells != 1 || data->fdt.cells[0] >= sc->nirqs)
+ return (EINVAL);
+
+ *isrcp = PIC_INTR_ISRC(sc, data->fdt.cells[0]);
+ return (0);
+#else
+ return (EINVAL);
+#endif
+}
+
+static void
+mtk_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ u_int irq;
+
+ irq = ((struct mtk_pic_irqsrc *)isrc)->irq;
+ pic_irq_unmask(device_get_softc(dev), irq);
+}
+
+static void
+mtk_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ u_int irq;
+
+ irq = ((struct mtk_pic_irqsrc *)isrc)->irq;
+ pic_irq_mask(device_get_softc(dev), irq);
+}
+
+static void
+mtk_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ mtk_pic_disable_intr(dev, isrc);
+}
+
+static void
+mtk_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ mtk_pic_enable_intr(dev, isrc);
+}
+
+static void
+mtk_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+}
+
+static device_method_t mtk_pic_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, mtk_pic_probe),
+ DEVMETHOD(device_attach, mtk_pic_attach),
+ /* Interrupt controller interface */
+ DEVMETHOD(pic_disable_intr, mtk_pic_disable_intr),
+ DEVMETHOD(pic_enable_intr, mtk_pic_enable_intr),
+ DEVMETHOD(pic_map_intr, mtk_pic_map_intr),
+ DEVMETHOD(pic_post_filter, mtk_pic_post_filter),
+ DEVMETHOD(pic_post_ithread, mtk_pic_post_ithread),
+ DEVMETHOD(pic_pre_ithread, mtk_pic_pre_ithread),
+ { 0, 0 }
+};
+
+static driver_t mtk_pic_driver = {
+ "intc",
+ mtk_pic_methods,
+ sizeof(struct mtk_pic_softc),
+};
+
+static devclass_t mtk_pic_devclass;
+
+EARLY_DRIVER_MODULE(intc_v2, simplebus, mtk_pic_driver, mtk_pic_devclass, 0, 0,
+ BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
OpenPOWER on IntegriCloud