summaryrefslogtreecommitdiffstats
path: root/sys/mips
diff options
context:
space:
mode:
Diffstat (limited to 'sys/mips')
-rw-r--r--sys/mips/mediatek/fdt_reset.c125
-rw-r--r--sys/mips/mediatek/fdt_reset.h49
-rw-r--r--sys/mips/mediatek/fdt_reset_if.m58
-rw-r--r--sys/mips/mediatek/mtk_clock.c156
-rw-r--r--sys/mips/mediatek/mtk_dotg.c220
-rw-r--r--sys/mips/mediatek/mtk_ehci.c223
-rw-r--r--sys/mips/mediatek/mtk_gpio_v1.c675
-rw-r--r--sys/mips/mediatek/mtk_gpio_v2.c675
-rw-r--r--sys/mips/mediatek/mtk_intr_gic.c377
-rw-r--r--sys/mips/mediatek/mtk_intr_v1.c353
-rw-r--r--sys/mips/mediatek/mtk_intr_v2.c348
-rw-r--r--sys/mips/mediatek/mtk_machdep.c286
-rw-r--r--sys/mips/mediatek/mtk_ohci.c223
-rw-r--r--sys/mips/mediatek/mtk_pcie.c1489
-rw-r--r--sys/mips/mediatek/mtk_pcie.h164
-rw-r--r--sys/mips/mediatek/mtk_pinctrl.c114
-rw-r--r--sys/mips/mediatek/mtk_reset.c139
-rw-r--r--sys/mips/mediatek/mtk_soc.c438
-rw-r--r--sys/mips/mediatek/mtk_soc.h130
-rw-r--r--sys/mips/mediatek/mtk_spi_v1.c351
-rw-r--r--sys/mips/mediatek/mtk_spi_v1.h71
-rw-r--r--sys/mips/mediatek/mtk_spi_v2.c357
-rw-r--r--sys/mips/mediatek/mtk_spi_v2.h55
-rw-r--r--sys/mips/mediatek/mtk_sysctl.c191
-rw-r--r--sys/mips/mediatek/mtk_sysctl.h59
-rw-r--r--sys/mips/mediatek/mtk_usb_phy.c324
-rw-r--r--sys/mips/mediatek/mtk_usb_phy.h66
-rw-r--r--sys/mips/mediatek/mtk_xhci.c298
-rw-r--r--sys/mips/mediatek/uart_dev_mtk.c552
-rw-r--r--sys/mips/mediatek/uart_dev_mtk.h126
-rw-r--r--sys/mips/mediatek/uart_dev_mtk_ns8250.c113
-rw-r--r--sys/mips/mips/db_disasm.c6
-rw-r--r--sys/mips/mips/db_trace.c4
-rw-r--r--sys/mips/mips/mips_pic.c307
-rw-r--r--sys/mips/mips/nexus.c19
-rw-r--r--sys/mips/mips/ofw_machdep.c2
36 files changed, 8923 insertions, 220 deletions
diff --git a/sys/mips/mediatek/fdt_reset.c b/sys/mips/mediatek/fdt_reset.c
new file mode 100644
index 0000000..5ff2758
--- /dev/null
+++ b/sys/mips/mediatek/fdt_reset.c
@@ -0,0 +1,125 @@
+/*-
+ * Copyright (c) 2016 Stanislav Galabov
+ * Copyright (c) 2014 Ian Lepore <ian@freebsd.org>
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/systm.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "fdt_reset_if.h"
+#include <mips/mediatek/fdt_reset.h>
+
+/*
+ * Loop through all the tuples in the resets= property for a device, asserting
+ * or deasserting each reset.
+ *
+ * Be liberal about errors for now: warn about a failure to (de)assert but keep
+ * trying with any other resets in the list. Return ENXIO if any errors were
+ * found, and let the caller decide whether the problem is fatal.
+ */
+static int
+assert_deassert_all(device_t consumer, boolean_t assert)
+{
+ phandle_t rnode;
+ device_t resetdev;
+ int resetnum, err, i, ncells;
+ uint32_t *resets;
+ boolean_t anyerrors;
+
+ rnode = ofw_bus_get_node(consumer);
+ ncells = OF_getencprop_alloc(rnode, "resets", sizeof(*resets),
+ (void **)&resets);
+ if (!assert && ncells < 2) {
+ device_printf(consumer, "Warning: No resets specified in fdt "
+ "data; device may not function.");
+ return (ENXIO);
+ }
+ anyerrors = false;
+ for (i = 0; i < ncells; i += 2) {
+ resetdev = OF_device_from_xref(resets[i]);
+ resetnum = resets[i + 1];
+ if (resetdev == NULL) {
+ if (!assert)
+ device_printf(consumer, "Warning: can not find "
+ "driver for reset number %u; device may "
+ "not function\n", resetnum);
+ anyerrors = true;
+ continue;
+ }
+ if (assert)
+ err = FDT_RESET_ASSERT(resetdev, resetnum);
+ else
+ err = FDT_RESET_DEASSERT(resetdev, resetnum);
+ if (err != 0) {
+ if (!assert)
+ device_printf(consumer, "Warning: failed to "
+ "deassert reset number %u; device may not "
+ "function\n", resetnum);
+ anyerrors = true;
+ }
+ }
+ free(resets, M_OFWPROP);
+ return (anyerrors ? ENXIO : 0);
+}
+
+int
+fdt_reset_assert_all(device_t consumer)
+{
+
+ return (assert_deassert_all(consumer, true));
+}
+
+int
+fdt_reset_deassert_all(device_t consumer)
+{
+
+ return (assert_deassert_all(consumer, false));
+}
+
+void
+fdt_reset_register_provider(device_t provider)
+{
+
+ OF_device_register_xref(
+ OF_xref_from_node(ofw_bus_get_node(provider)), provider);
+}
+
+void
+fdt_reset_unregister_provider(device_t provider)
+{
+
+ OF_device_register_xref(OF_xref_from_device(provider), NULL);
+}
+
diff --git a/sys/mips/mediatek/fdt_reset.h b/sys/mips/mediatek/fdt_reset.h
new file mode 100644
index 0000000..195beaa
--- /dev/null
+++ b/sys/mips/mediatek/fdt_reset.h
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2016 Stanislav Galabov
+ * Copyright (c) 2014 Ian Lepore <ian@freebsd.org>
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef DEV_FDT_RESET_H
+#define DEV_FDT_RESET_H
+
+#include "fdt_reset_if.h"
+
+/*
+ * Look up "resets" property in consumer's fdt data and assert or deassert all
+ * configured resets.
+ */
+int fdt_reset_assert_all(device_t consumer);
+int fdt_reset_deassert_all(device_t consumer);
+
+/*
+ * [Un]register the given device instance as a driver that implements the
+ * fdt_clock interface.
+ */
+void fdt_reset_register_provider(device_t provider);
+void fdt_reset_unregister_provider(device_t provider);
+
+#endif /* DEV_FDT_RESET_H */
+
diff --git a/sys/mips/mediatek/fdt_reset_if.m b/sys/mips/mediatek/fdt_reset_if.m
new file mode 100644
index 0000000..2bde7b7
--- /dev/null
+++ b/sys/mips/mediatek/fdt_reset_if.m
@@ -0,0 +1,58 @@
+#-
+# Copyright (c) 2016 Stanislav Galabov
+# Copyright (c) 2014 Ian Lepore
+# 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.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 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.
+#
+# $FreeBSD$
+#
+
+#include <sys/types.h>
+
+#
+# This is the interface that fdt_reset drivers provide to other drivers.
+# In this context, reset refers to a reset signal provided to some other
+# hardware component within the system. They are most often found within
+# embedded processors that have on-chip IO controllers.
+#
+
+INTERFACE fdt_reset;
+
+#
+# Enable/assert/apply the specified reset.
+# Returns 0 on success or a standard errno value.
+#
+METHOD int assert {
+ device_t provider;
+ int index;
+};
+
+#
+# Disable/de-assert/remove the specified reset.
+# Returns 0 on success or a standard errno value.
+#
+METHOD int deassert {
+ device_t provider;
+ int index;
+};
+
diff --git a/sys/mips/mediatek/mtk_clock.c b/sys/mips/mediatek/mtk_clock.c
new file mode 100644
index 0000000..83433fb
--- /dev/null
+++ b/sys/mips/mediatek/mtk_clock.c
@@ -0,0 +1,156 @@
+/*-
+ * Copyright (c) 2016 Stanislav Galabov
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.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 <dev/fdt/fdt_clock.h>
+
+#include <mips/mediatek/mtk_sysctl.h>
+
+#include "fdt_clock_if.h"
+
+static const struct ofw_compat_data compat_data[] = {
+ { "ralink,rt2880-clock", 1 },
+
+ /* Sentinel */
+ { NULL, 0 }
+};
+
+static int
+mtk_clock_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 Clock Controller");
+
+ return (0);
+}
+
+static int
+mtk_clock_attach(device_t dev)
+{
+
+ if (device_get_unit(dev) != 0) {
+ device_printf(dev, "Only one clock control allowed\n");
+ return (ENXIO);
+ }
+
+ fdt_clock_register_provider(dev);
+
+ return (0);
+}
+
+#define CLOCK_ENABLE 1
+#define CLOCK_DISABLE 0
+
+static int
+mtk_clock_set(device_t dev, int index, int value)
+{
+ uint32_t mask;
+
+ /* Clock config register holds 32 clock gating bits */
+ if (index < 0 || index > 31)
+ return (EINVAL);
+
+ mask = (1u << index);
+
+ if (value == CLOCK_ENABLE)
+ mtk_sysctl_clr_set(SYSCTL_CLKCFG1, 0, mask);
+ else
+ mtk_sysctl_clr_set(SYSCTL_CLKCFG1, mask, 0);
+
+ return (0);
+}
+
+static int
+mtk_clock_enable(device_t dev, int index)
+{
+
+ return mtk_clock_set(dev, index, CLOCK_ENABLE);
+}
+
+static int
+mtk_clock_disable(device_t dev, int index)
+{
+
+ return mtk_clock_set(dev, index, CLOCK_DISABLE);
+}
+
+static int
+mtk_clock_get_info(device_t dev, int index, struct fdt_clock_info *info)
+{
+ uint32_t mask;
+
+ if (index < 0 || index > 31 || info == NULL)
+ return (EINVAL);
+
+ if (mtk_sysctl_get(SYSCTL_CLKCFG1) & mask)
+ info->flags = FDT_CIFLAG_RUNNING;
+ else
+ info->flags = 0;
+
+ return (0);
+}
+
+static device_method_t mtk_clock_methods[] = {
+ DEVMETHOD(device_probe, mtk_clock_probe),
+ DEVMETHOD(device_attach, mtk_clock_attach),
+
+ /* fdt_clock interface */
+ DEVMETHOD(fdt_clock_enable, mtk_clock_enable),
+ DEVMETHOD(fdt_clock_disable, mtk_clock_disable),
+ DEVMETHOD(fdt_clock_get_info, mtk_clock_get_info),
+
+ DEVMETHOD_END
+};
+
+static driver_t mtk_clock_driver = {
+ "clkctrl",
+ mtk_clock_methods,
+ 0,
+};
+static devclass_t mtk_clock_devclass;
+
+EARLY_DRIVER_MODULE(mtk_clock, simplebus, mtk_clock_driver, mtk_clock_devclass,
+ 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_EARLY);
+
+MODULE_DEPEND(mtk_clock, mtk_sysctl, 1, 1, 1);
diff --git a/sys/mips/mediatek/mtk_dotg.c b/sys/mips/mediatek/mtk_dotg.c
new file mode 100644
index 0000000..d7421ad
--- /dev/null
+++ b/sys/mips/mediatek/mtk_dotg.c
@@ -0,0 +1,220 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2015-2016 Stanislav Galabov. All rights reserved.
+ * Copyright (c) 2010,2011 Aleksandr Rybalko. All rights reserved.
+ * Copyright (c) 2007-2008 Hans Petter Selasky. 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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 <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+#include <sys/rman.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+
+#include <dev/usb/controller/dwc_otg.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#define MEM_RID 0
+
+static device_probe_t dotg_fdt_probe;
+static device_attach_t dotg_fdt_attach;
+static device_detach_t dotg_fdt_detach;
+
+static int
+dotg_fdt_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "ralink,rt3050-otg"))
+ return (ENXIO);
+
+ device_set_desc(dev, "MTK DWC-OTG USB Controller");
+ return (0);
+}
+
+static int
+dotg_fdt_attach(device_t dev)
+{
+ struct dwc_otg_softc *sc = device_get_softc(dev);
+ int err, rid;
+
+ /* setup controller interface softc */
+
+ /* initialise some bus fields */
+ sc->sc_mode = DWC_MODE_HOST;
+ sc->sc_bus.parent = dev;
+ sc->sc_bus.devices = sc->sc_devices;
+ sc->sc_bus.devices_max = DWC_OTG_MAX_DEVICES;
+ sc->sc_bus.dma_bits = 32;
+
+ /* get all DMA memory */
+ if (usb_bus_mem_alloc_all(&sc->sc_bus,
+ USB_GET_DMA_TAG(dev), NULL)) {
+ printf("No mem\n");
+ return (ENOMEM);
+ }
+ rid = 0;
+ sc->sc_io_res =
+ bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (!(sc->sc_io_res)) {
+ printf("Can`t alloc MEM\n");
+ goto error;
+ }
+ sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
+ sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
+ sc->sc_io_size = rman_get_size(sc->sc_io_res);
+
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+ &rid, RF_ACTIVE);
+ if (!(sc->sc_irq_res)) {
+ printf("Can`t alloc IRQ\n");
+ goto error;
+ }
+
+ sc->sc_bus.bdev = device_add_child(dev, "usbus", -1);
+ if (!(sc->sc_bus.bdev)) {
+ printf("Can`t add usbus\n");
+ goto error;
+ }
+ device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+
+ err = bus_setup_intr(dev, sc->sc_irq_res,
+ INTR_TYPE_TTY | INTR_MPSAFE, dwc_otg_filter_interrupt,
+ dwc_otg_interrupt, sc, &sc->sc_intr_hdl);
+ if (err) {
+ sc->sc_intr_hdl = NULL;
+ printf("Can`t set IRQ handle\n");
+ goto error;
+ }
+
+ err = dwc_otg_init(sc);
+ if (err) printf("dotg_init fail\n");
+ if (!err) {
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+ if (err) printf("device_probe_and_attach fail %d\n", err);
+ }
+ if (err) {
+ goto error;
+ }
+ return (0);
+
+error:
+ dotg_fdt_detach(dev);
+ return (ENXIO);
+}
+
+static int
+dotg_fdt_detach(device_t dev)
+{
+ struct dwc_otg_softc *sc = device_get_softc(dev);
+ device_t bdev;
+ int err;
+
+ if (sc->sc_bus.bdev) {
+ bdev = sc->sc_bus.bdev;
+ device_detach(bdev);
+ device_delete_child(dev, bdev);
+ }
+ /* during module unload there are lots of children leftover */
+ device_delete_children(dev);
+
+ if (sc->sc_irq_res && sc->sc_intr_hdl) {
+ /*
+ * only call dotg_fdt_uninit() after dotg_fdt_init()
+ */
+ dwc_otg_uninit(sc);
+
+ err = bus_teardown_intr(dev, sc->sc_irq_res,
+ sc->sc_intr_hdl);
+ sc->sc_intr_hdl = NULL;
+ }
+ if (sc->sc_irq_res) {
+ bus_release_resource(dev, SYS_RES_IRQ, 0,
+ sc->sc_irq_res);
+ sc->sc_irq_res = NULL;
+ }
+ if (sc->sc_io_res) {
+ bus_release_resource(dev, SYS_RES_MEMORY, 0,
+ sc->sc_io_res);
+ sc->sc_io_res = NULL;
+ }
+ usb_bus_mem_free_all(&sc->sc_bus, NULL);
+
+ return (0);
+}
+
+static device_method_t dotg_fdt_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, dotg_fdt_probe),
+ DEVMETHOD(device_attach, dotg_fdt_attach),
+ DEVMETHOD(device_detach, dotg_fdt_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ DEVMETHOD_END
+};
+
+static driver_t dotg_fdt_driver = {
+ .name = "dwcotg",
+ .methods = dotg_fdt_methods,
+ .size = sizeof(struct dwc_otg_softc),
+};
+
+static devclass_t dotg_fdt_devclass;
+
+DRIVER_MODULE(dotg, simplebus, dotg_fdt_driver, dotg_fdt_devclass, 0, 0);
diff --git a/sys/mips/mediatek/mtk_ehci.c b/sys/mips/mediatek/mtk_ehci.c
new file mode 100644
index 0000000..109a32a
--- /dev/null
+++ b/sys/mips/mediatek/mtk_ehci.c
@@ -0,0 +1,223 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2015 Stanislav Galabov. All rights reserved.
+ * Copyright (c) 2010,2011 Aleksandr Rybalko. All rights reserved.
+ * Copyright (c) 2007-2008 Hans Petter Selasky. 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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 <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+#include <sys/rman.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+
+#include <dev/usb/controller/ehci.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#define EHCI_HC_DEVSTR "MTK USB 2.0 Controller"
+
+static device_probe_t ehci_fdt_probe;
+static device_attach_t ehci_fdt_attach;
+static device_detach_t ehci_fdt_detach;
+
+static int
+ehci_fdt_probe(device_t self)
+{
+
+ if (!ofw_bus_status_okay(self))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(self, "ralink,rt3xxx-ehci"))
+ return (ENXIO);
+
+ device_set_desc(self, EHCI_HC_DEVSTR);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ehci_fdt_attach(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+ int err;
+ int rid;
+
+ /* initialise some bus fields */
+ sc->sc_bus.parent = self;
+ sc->sc_bus.devices = sc->sc_devices;
+ sc->sc_bus.devices_max = EHCI_MAX_DEVICES;
+ sc->sc_bus.dma_bits = 32;
+
+ /* get all DMA memory */
+ if (usb_bus_mem_alloc_all(&sc->sc_bus,
+ USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) {
+ printf("No mem\n");
+ return (ENOMEM);
+ }
+
+ rid = 0;
+ sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_io_res) {
+ device_printf(self, "Could not map memory\n");
+ goto error;
+ }
+ sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
+ sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
+ sc->sc_io_size = rman_get_size(sc->sc_io_res);
+
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->sc_irq_res == NULL) {
+ device_printf(self, "Could not allocate irq\n");
+ goto error;
+ }
+
+ sc->sc_bus.bdev = device_add_child(self, "usbus", -1);
+ if (!(sc->sc_bus.bdev)) {
+ device_printf(self, "Could not add USB device\n");
+ goto error;
+ }
+ device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+ device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR);
+
+ sprintf(sc->sc_vendor, "MediaTek");
+
+ err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl);
+ if (err) {
+ device_printf(self, "Could not setup irq, %d\n", err);
+ sc->sc_intr_hdl = NULL;
+ goto error;
+ }
+
+ err = ehci_init(sc);
+ if (!err) {
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+ }
+ if (err) {
+ device_printf(self, "USB init failed err=%d\n", err);
+ goto error;
+ }
+ return (0);
+
+error:
+ ehci_fdt_detach(self);
+ return (ENXIO);
+}
+
+static int
+ehci_fdt_detach(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+ device_t bdev;
+ int err;
+
+ if (sc->sc_bus.bdev) {
+ bdev = sc->sc_bus.bdev;
+ device_detach(bdev);
+ device_delete_child(self, bdev);
+ }
+ /* during module unload there are lots of children leftover */
+ device_delete_children(self);
+
+ if (sc->sc_irq_res && sc->sc_intr_hdl) {
+ /*
+ * only call ehci_detach() after ehci_init()
+ */
+ ehci_detach(sc);
+
+ err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl);
+ if (err)
+ device_printf(self, "Could not tear down irq, %d\n",
+ err);
+ sc->sc_intr_hdl = NULL;
+ }
+ if (sc->sc_irq_res) {
+ bus_release_resource(self, SYS_RES_IRQ, 0,
+ sc->sc_irq_res);
+ sc->sc_irq_res = NULL;
+ }
+ if (sc->sc_io_res) {
+ bus_release_resource(self, SYS_RES_MEMORY, 0,
+ sc->sc_io_res);
+ sc->sc_io_res = NULL;
+ }
+ usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc);
+
+ return (0);
+}
+
+static device_method_t ehci_fdt_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ehci_fdt_probe),
+ DEVMETHOD(device_attach, ehci_fdt_attach),
+ DEVMETHOD(device_detach, ehci_fdt_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ DEVMETHOD_END
+};
+
+static driver_t ehci_fdt_driver = {
+ .name = "ehci",
+ .methods = ehci_fdt_methods,
+ .size = sizeof(ehci_softc_t),
+};
+
+static devclass_t ehci_fdt_devclass;
+
+DRIVER_MODULE(ehci, simplebus, ehci_fdt_driver, ehci_fdt_devclass, 0, 0);
diff --git a/sys/mips/mediatek/mtk_gpio_v1.c b/sys/mips/mediatek/mtk_gpio_v1.c
new file mode 100644
index 0000000..29b2258
--- /dev/null
+++ b/sys/mips/mediatek/mtk_gpio_v1.c
@@ -0,0 +1,675 @@
+/*-
+ * Copyright 2016 Stanislav Galabov
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/resource.h>
+#include <sys/gpio.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <mips/mediatek/mtk_soc.h>
+
+#include <dev/gpio/gpiobusvar.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <gnu/dts/include/dt-bindings/interrupt-controller/irq.h>
+
+#include "gpio_if.h"
+#include "pic_if.h"
+
+#define MTK_GPIO_PINS 32
+
+struct mtk_gpio_pin_irqsrc {
+ struct intr_irqsrc isrc;
+ u_int irq;
+};
+
+struct mtk_gpio_pin {
+ uint32_t pin_caps;
+ uint32_t pin_flags;
+ enum intr_trigger intr_trigger;
+ enum intr_polarity intr_polarity;
+ char pin_name[GPIOMAXNAME];
+ struct mtk_gpio_pin_irqsrc pin_irqsrc;
+};
+
+struct mtk_gpio_softc {
+ device_t dev;
+ device_t busdev;
+ struct resource *res[2];
+ struct mtx mtx;
+ struct mtk_gpio_pin pins[MTK_GPIO_PINS];
+ void *intrhand;
+
+ uint32_t num_pins;
+ uint8_t do_remap;
+};
+
+#define PIC_INTR_ISRC(sc, irq) (&(sc)->pins[(irq)].pin_irqsrc.isrc)
+
+static struct resource_spec mtk_gpio_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE },
+ { -1, 0 }
+};
+
+static int mtk_gpio_probe(device_t dev);
+static int mtk_gpio_attach(device_t dev);
+static int mtk_gpio_detach(device_t dev);
+static int mtk_gpio_intr(void *arg);
+
+#define MTK_GPIO_LOCK(sc) mtx_lock_spin(&(sc)->mtx)
+#define MTK_GPIO_UNLOCK(sc) mtx_unlock_spin(&(sc)->mtx)
+#define MTK_GPIO_LOCK_INIT(sc) \
+ mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \
+ "mtk_gpio", MTX_SPIN)
+#define MTK_GPIO_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx)
+
+#define MTK_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val))
+#define MTK_READ_4(sc, reg) bus_read_4((sc)->res[0], (reg))
+
+/* Register definitions */
+#define GPIO_PIOINT(_sc) 0x0000
+#define GPIO_PIOEDGE(_sc) 0x0004
+#define GPIO_PIORENA(_sc) 0x0008
+#define GPIO_PIOFENA(_sc) 0x000C
+#define GPIO_PIODATA(_sc) ((_sc)->do_remap ? 0x0020 : 0x0010)
+#define GPIO_PIODIR(_sc) ((_sc)->do_remap ? 0x0024 : 0x0014)
+#define GPIO_PIOPOL(_sc) ((_sc)->do_remap ? 0x0028 : 0x0018)
+#define GPIO_PIOSET(_sc) ((_sc)->do_remap ? 0x002C : 0x001C)
+#define GPIO_PIORESET(_sc) ((_sc)->do_remap ? 0x0030 : 0x0020)
+#define GPIO_PIOTOG(_sc) ((_sc)->do_remap ? 0x0034 : 0x0024)
+
+static struct ofw_compat_data compat_data[] = {
+ { "ralink,rt2880-gpio", 1 },
+ { "ralink,rt3050-gpio", 1 },
+ { "ralink,rt3352-gpio", 1 },
+ { "ralink,rt3883-gpio", 1 },
+ { "ralink,rt5350-gpio", 1 },
+ { "ralink,mt7620a-gpio", 1 },
+ { NULL, 0 }
+};
+
+static int
+mtk_gpio_probe(device_t dev)
+{
+ phandle_t node;
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ node = ofw_bus_get_node(dev);
+ if (!OF_hasprop(node, "gpio-controller"))
+ return (ENXIO);
+
+ device_set_desc(dev, "MTK GPIO Controller (v1)");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+mtk_pic_register_isrcs(struct mtk_gpio_softc *sc)
+{
+ int error;
+ uint32_t irq;
+ struct intr_irqsrc *isrc;
+ const char *name;
+
+ name = device_get_nameunit(sc->dev);
+ for (irq = 0; irq < sc->num_pins; irq++) {
+ sc->pins[irq].pin_irqsrc.irq = irq;
+ isrc = PIC_INTR_ISRC(sc, irq);
+ error = intr_isrc_register(isrc, sc->dev, 0, "%s", name);
+ if (error != 0) {
+ /* XXX call intr_isrc_deregister */
+ device_printf(sc->dev, "%s failed", __func__);
+ return (error);
+ }
+ }
+
+ return (0);
+}
+
+static int
+mtk_gpio_pin_set_direction(struct mtk_gpio_softc *sc, uint32_t pin,
+ uint32_t dir)
+{
+ uint32_t regval, mask = (1u << pin);
+
+ if (!(sc->pins[pin].pin_caps & dir))
+ return (EINVAL);
+
+ regval = MTK_READ_4(sc, GPIO_PIODIR(sc));
+ if (dir == GPIO_PIN_INPUT)
+ regval &= ~mask;
+ else
+ regval |= mask;
+ MTK_WRITE_4(sc, GPIO_PIODIR(sc), regval);
+
+ sc->pins[pin].pin_flags &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
+ sc->pins[pin].pin_flags |= dir;
+
+ return (0);
+}
+
+static int
+mtk_gpio_pin_set_invert(struct mtk_gpio_softc *sc, uint32_t pin, uint32_t val)
+{
+ uint32_t regval, mask = (1u << pin);
+
+ regval = MTK_READ_4(sc, GPIO_PIOPOL(sc));
+ if (val)
+ regval |= mask;
+ else
+ regval &= ~mask;
+ MTK_WRITE_4(sc, GPIO_PIOPOL(sc), regval);
+ sc->pins[pin].pin_flags &= ~(GPIO_PIN_INVIN | GPIO_PIN_INVOUT);
+ sc->pins[pin].pin_flags |= val;
+
+ return (0);
+}
+
+static void
+mtk_gpio_pin_probe(struct mtk_gpio_softc *sc, uint32_t pin)
+{
+ uint32_t mask = (1u << pin);
+ uint32_t val;
+
+ /* Clear cached gpio config */
+ sc->pins[pin].pin_flags = 0;
+
+ val = MTK_READ_4(sc, GPIO_PIORENA(sc)) |
+ MTK_READ_4(sc, GPIO_PIOFENA(sc));
+ if (val & mask) {
+ /* Pin is in interrupt mode */
+ sc->pins[pin].intr_trigger = INTR_TRIGGER_EDGE;
+ val = MTK_READ_4(sc, GPIO_PIORENA(sc));
+ if (val & mask)
+ sc->pins[pin].intr_polarity = INTR_POLARITY_HIGH;
+ else
+ sc->pins[pin].intr_polarity = INTR_POLARITY_LOW;
+ }
+
+ val = MTK_READ_4(sc, GPIO_PIODIR(sc));
+ if (val & mask)
+ sc->pins[pin].pin_flags |= GPIO_PIN_OUTPUT;
+ else
+ sc->pins[pin].pin_flags |= GPIO_PIN_INPUT;
+
+ val = MTK_READ_4(sc, GPIO_PIOPOL(sc));
+ if (val & mask) {
+ if (sc->pins[pin].pin_flags & GPIO_PIN_INPUT) {
+ sc->pins[pin].pin_flags |= GPIO_PIN_INVIN;
+ } else {
+ sc->pins[pin].pin_flags |= GPIO_PIN_INVOUT;
+ }
+ }
+}
+
+static int
+mtk_gpio_attach(device_t dev)
+{
+ struct mtk_gpio_softc *sc;
+ phandle_t node;
+ uint32_t i, num_pins;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ if (bus_alloc_resources(dev, mtk_gpio_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources for device\n");
+ return (ENXIO);
+ }
+
+ MTK_GPIO_LOCK_INIT(sc);
+
+ node = ofw_bus_get_node(dev);
+
+ if (OF_hasprop(node, "clocks"))
+ mtk_soc_start_clock(dev);
+ if (OF_hasprop(node, "resets"))
+ mtk_soc_reset_device(dev);
+
+ if (OF_hasprop(node, "mtk,register-gap")) {
+ device_printf(dev, "<register gap>\n");
+ sc->do_remap = 1;
+ } else {
+ device_printf(dev, "<no register gap>\n");
+ sc->do_remap = 0;
+ }
+
+ if (OF_hasprop(node, "mtk,num-pins") && (OF_getencprop(node,
+ "mtk,num-pins", &num_pins, sizeof(num_pins)) >= 0))
+ sc->num_pins = num_pins;
+ else
+ sc->num_pins = MTK_GPIO_PINS;
+
+ for (i = 0; i < num_pins; i++) {
+ sc->pins[i].pin_caps |= GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |
+ GPIO_PIN_INVIN | GPIO_PIN_INVOUT;
+ sc->pins[i].intr_polarity = INTR_POLARITY_HIGH;
+ sc->pins[i].intr_trigger = INTR_TRIGGER_EDGE;
+
+ snprintf(sc->pins[i].pin_name, GPIOMAXNAME - 1, "gpio%c%d",
+ device_get_unit(dev) + 'a', i);
+ sc->pins[i].pin_name[GPIOMAXNAME - 1] = '\0';
+
+ mtk_gpio_pin_probe(sc, i);
+ }
+
+ if (mtk_pic_register_isrcs(sc) != 0) {
+ device_printf(dev, "could not register PIC ISRCs\n");
+ goto fail;
+ }
+
+ if (intr_pic_register(dev, OF_xref_from_node(node)) != 0) {
+ device_printf(dev, "could not register PIC\n");
+ goto fail;
+ }
+
+ if (bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE,
+ mtk_gpio_intr, NULL, sc, &sc->intrhand) != 0)
+ goto fail_pic;
+
+ sc->busdev = gpiobus_attach_bus(dev);
+ if (sc->busdev == NULL)
+ goto fail_pic;
+
+ return (0);
+fail_pic:
+ intr_pic_deregister(dev, OF_xref_from_node(node));
+fail:
+ if(sc->intrhand != NULL)
+ bus_teardown_intr(dev, sc->res[1], sc->intrhand);
+ bus_release_resources(dev, mtk_gpio_spec, sc->res);
+ MTK_GPIO_LOCK_DESTROY(sc);
+ return (ENXIO);
+}
+
+static int
+mtk_gpio_detach(device_t dev)
+{
+ struct mtk_gpio_softc *sc = device_get_softc(dev);
+ phandle_t node;
+
+ node = ofw_bus_get_node(dev);
+ intr_pic_deregister(dev, OF_xref_from_node(node));
+ if (sc->intrhand != NULL)
+ bus_teardown_intr(dev, sc->res[1], sc->intrhand);
+ bus_release_resources(dev, mtk_gpio_spec, sc->res);
+ MTK_GPIO_LOCK_DESTROY(sc);
+ return (0);
+}
+
+static device_t
+mtk_gpio_get_bus(device_t dev)
+{
+ struct mtk_gpio_softc *sc = device_get_softc(dev);
+
+ return (sc->busdev);
+}
+
+static int
+mtk_gpio_pin_max(device_t dev, int *maxpin)
+{
+ struct mtk_gpio_softc *sc = device_get_softc(dev);
+
+ *maxpin = sc->num_pins - 1;
+
+ return (0);
+}
+
+static int
+mtk_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+ struct mtk_gpio_softc *sc = device_get_softc(dev);
+
+ if (pin >= sc->num_pins)
+ return (EINVAL);
+
+ MTK_GPIO_LOCK(sc);
+ *caps = sc->pins[pin].pin_caps;
+ MTK_GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+mtk_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
+{
+ struct mtk_gpio_softc *sc = device_get_softc(dev);
+
+ if (pin >= sc->num_pins)
+ return (EINVAL);
+
+ MTK_GPIO_LOCK(sc);
+ *flags = sc->pins[pin].pin_flags;
+ MTK_GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+mtk_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+ struct mtk_gpio_softc *sc = device_get_softc(dev);
+
+ if (pin >= sc->num_pins)
+ return (EINVAL);
+
+ strncpy(name, sc->pins[pin].pin_name, GPIOMAXNAME - 1);
+ name[GPIOMAXNAME - 1] = '\0';
+
+ return (0);
+}
+
+static int
+mtk_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+ struct mtk_gpio_softc *sc;
+ int retval;
+
+ sc = device_get_softc(dev);
+
+ if (pin >= sc->num_pins)
+ return (EINVAL);
+
+ MTK_GPIO_LOCK(sc);
+ retval = mtk_gpio_pin_set_direction(sc, pin,
+ flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT));
+ if (retval == 0)
+ retval = mtk_gpio_pin_set_invert(sc, pin,
+ flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT));
+ MTK_GPIO_UNLOCK(sc);
+
+ return (retval);
+}
+
+static int
+mtk_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
+{
+ struct mtk_gpio_softc *sc;
+ int ret;
+
+ sc = device_get_softc(dev);
+ ret = 0;
+
+ if (pin >= sc->num_pins)
+ return (EINVAL);
+
+ MTK_GPIO_LOCK(sc);
+ if(!(sc->pins[pin].pin_flags & GPIO_PIN_OUTPUT)) {
+ ret = EINVAL;
+ goto out;
+ }
+
+ if (value)
+ MTK_WRITE_4(sc, GPIO_PIOSET(sc), (1u << pin));
+ else
+ MTK_WRITE_4(sc, GPIO_PIORESET(sc), (1u << pin));
+
+out:
+ MTK_GPIO_UNLOCK(sc);
+ return (ret);
+}
+
+static int
+mtk_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
+{
+ struct mtk_gpio_softc *sc;
+ uint32_t data;
+ int ret;
+
+ sc = device_get_softc(dev);
+ ret = 0;
+
+ if (pin >= sc->num_pins)
+ return (EINVAL);
+
+ MTK_GPIO_LOCK(sc);
+ if(!(sc->pins[pin].pin_flags & GPIO_PIN_INPUT)) {
+ ret = EINVAL;
+ goto out;
+ }
+ data = MTK_READ_4(sc, GPIO_PIODATA(sc));
+ *val = (data & (1u << pin)) ? 1 : 0;
+
+out:
+ MTK_GPIO_UNLOCK(sc);
+ return (ret);
+}
+
+static int
+mtk_gpio_pin_toggle(device_t dev, uint32_t pin)
+{
+ struct mtk_gpio_softc *sc;
+ int ret;
+
+ if (pin >= sc->num_pins)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ ret = 0;
+
+ MTK_GPIO_LOCK(sc);
+ if (!(sc->pins[pin].pin_flags & GPIO_PIN_OUTPUT)) {
+ ret = EINVAL;
+ goto out;
+ }
+ MTK_WRITE_4(sc, GPIO_PIOTOG(sc), (1u << pin));
+
+out:
+ MTK_GPIO_UNLOCK(sc);
+
+ return (ret);
+}
+
+static int
+mtk_gpio_pic_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+ struct mtk_gpio_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->num_pins)
+ return (EINVAL);
+
+ *isrcp = PIC_INTR_ISRC(sc, data->fdt.cells[0]);
+ return (0);
+}
+
+static void
+mtk_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct mtk_gpio_softc *sc;
+ struct mtk_gpio_pin_irqsrc *pisrc;
+ uint32_t pin, mask, val;
+
+ sc = device_get_softc(dev);
+
+ pisrc = (struct mtk_gpio_pin_irqsrc *)isrc;
+ pin = pisrc->irq;
+ mask = 1u << pin;
+
+ MTK_GPIO_LOCK(sc);
+
+ if (sc->pins[pin].intr_polarity == INTR_POLARITY_LOW) {
+ val = MTK_READ_4(sc, GPIO_PIORENA(sc)) & ~mask;
+ MTK_WRITE_4(sc, GPIO_PIORENA(sc), val);
+ val = MTK_READ_4(sc, GPIO_PIOFENA(sc)) | mask;
+ MTK_WRITE_4(sc, GPIO_PIOFENA(sc), val);
+ } else {
+ val = MTK_READ_4(sc, GPIO_PIOFENA(sc)) & ~mask;
+ MTK_WRITE_4(sc, GPIO_PIOFENA(sc), val);
+ val = MTK_READ_4(sc, GPIO_PIORENA(sc)) | mask;
+ MTK_WRITE_4(sc, GPIO_PIORENA(sc), val);
+ }
+
+ MTK_GPIO_UNLOCK(sc);
+}
+
+static void
+mtk_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct mtk_gpio_softc *sc;
+ struct mtk_gpio_pin_irqsrc *pisrc;
+ uint32_t pin, mask, val;
+
+ sc = device_get_softc(dev);
+
+ pisrc = (struct mtk_gpio_pin_irqsrc *)isrc;
+ pin = pisrc->irq;
+ mask = 1u << pin;
+
+ MTK_GPIO_LOCK(sc);
+
+ val = MTK_READ_4(sc, GPIO_PIORENA(sc)) & ~mask;
+ MTK_WRITE_4(sc, GPIO_PIORENA(sc), val);
+ val = MTK_READ_4(sc, GPIO_PIOFENA(sc)) & ~mask;
+ MTK_WRITE_4(sc, GPIO_PIOFENA(sc), val);
+
+ MTK_GPIO_UNLOCK(sc);
+}
+
+static void
+mtk_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ mtk_gpio_pic_disable_intr(dev, isrc);
+}
+
+static void
+mtk_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ mtk_gpio_pic_enable_intr(dev, isrc);
+}
+
+static void
+mtk_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct mtk_gpio_softc *sc;
+ struct mtk_gpio_pin_irqsrc *pisrc;
+
+ pisrc = (struct mtk_gpio_pin_irqsrc *)isrc;
+ sc = device_get_softc(dev);
+ MTK_GPIO_LOCK(sc);
+ MTK_WRITE_4(sc, GPIO_PIOINT(sc), 1u << pisrc->irq);
+ MTK_GPIO_UNLOCK(sc);
+}
+
+static int
+mtk_gpio_intr(void *arg)
+{
+ struct mtk_gpio_softc *sc;
+ uint32_t i, interrupts;
+
+ sc = arg;
+ interrupts = MTK_READ_4(sc, GPIO_PIOINT(sc));
+
+ for (i = 0; interrupts != 0; i++, interrupts >>= 1) {
+ if ((interrupts & 0x1) == 0)
+ continue;
+ if (intr_isrc_dispatch(PIC_INTR_ISRC(sc, i),
+ curthread->td_intr_frame) != 0) {
+ device_printf(sc->dev, "spurious interrupt %d\n", i);
+ }
+ }
+
+ return (FILTER_HANDLED);
+}
+
+static phandle_t
+mtk_gpio_get_node(device_t bus, device_t dev)
+{
+
+ /* We only have one child, the GPIO bus, which needs our own node. */
+ return (ofw_bus_get_node(bus));
+}
+
+static device_method_t mtk_gpio_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, mtk_gpio_probe),
+ DEVMETHOD(device_attach, mtk_gpio_attach),
+ DEVMETHOD(device_detach, mtk_gpio_detach),
+
+ /* GPIO protocol */
+ DEVMETHOD(gpio_get_bus, mtk_gpio_get_bus),
+ DEVMETHOD(gpio_pin_max, mtk_gpio_pin_max),
+ DEVMETHOD(gpio_pin_getname, mtk_gpio_pin_getname),
+ DEVMETHOD(gpio_pin_getflags, mtk_gpio_pin_getflags),
+ DEVMETHOD(gpio_pin_getcaps, mtk_gpio_pin_getcaps),
+ DEVMETHOD(gpio_pin_setflags, mtk_gpio_pin_setflags),
+ DEVMETHOD(gpio_pin_get, mtk_gpio_pin_get),
+ DEVMETHOD(gpio_pin_set, mtk_gpio_pin_set),
+ DEVMETHOD(gpio_pin_toggle, mtk_gpio_pin_toggle),
+
+ /* Interrupt controller interface */
+ DEVMETHOD(pic_disable_intr, mtk_gpio_pic_disable_intr),
+ DEVMETHOD(pic_enable_intr, mtk_gpio_pic_enable_intr),
+ DEVMETHOD(pic_map_intr, mtk_gpio_pic_map_intr),
+ DEVMETHOD(pic_post_filter, mtk_gpio_pic_post_filter),
+ DEVMETHOD(pic_post_ithread, mtk_gpio_pic_post_ithread),
+ DEVMETHOD(pic_pre_ithread, mtk_gpio_pic_pre_ithread),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, mtk_gpio_get_node),
+
+ DEVMETHOD_END
+};
+
+static driver_t mtk_gpio_driver = {
+ "gpio",
+ mtk_gpio_methods,
+ sizeof(struct mtk_gpio_softc),
+};
+
+static devclass_t mtk_gpio_devclass;
+
+EARLY_DRIVER_MODULE(mtk_gpio_v1, simplebus, mtk_gpio_driver,
+ mtk_gpio_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
diff --git a/sys/mips/mediatek/mtk_gpio_v2.c b/sys/mips/mediatek/mtk_gpio_v2.c
new file mode 100644
index 0000000..282b112
--- /dev/null
+++ b/sys/mips/mediatek/mtk_gpio_v2.c
@@ -0,0 +1,675 @@
+/*-
+ * Copyright 2016 Stanislav Galabov
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/resource.h>
+#include <sys/gpio.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <mips/mediatek/mtk_soc.h>
+
+#include <dev/gpio/gpiobusvar.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <gnu/dts/include/dt-bindings/interrupt-controller/irq.h>
+
+#include "gpio_if.h"
+#include "pic_if.h"
+
+#define MTK_GPIO_PINS 32
+
+struct mtk_gpio_pin_irqsrc {
+ struct intr_irqsrc isrc;
+ u_int irq;
+};
+
+struct mtk_gpio_pin {
+ uint32_t pin_caps;
+ uint32_t pin_flags;
+ enum intr_trigger intr_trigger;
+ enum intr_polarity intr_polarity;
+ char pin_name[GPIOMAXNAME];
+ struct mtk_gpio_pin_irqsrc pin_irqsrc;
+};
+
+struct mtk_gpio_softc {
+ device_t dev;
+ device_t busdev;
+ struct resource *res[2];
+ struct mtx mtx;
+ struct mtk_gpio_pin pins[MTK_GPIO_PINS];
+ void *intrhand;
+
+ uint32_t num_pins;
+ uint32_t bank_id;
+};
+
+#define PIC_INTR_ISRC(sc, irq) (&(sc)->pins[(irq)].pin_irqsrc.isrc)
+
+static struct resource_spec mtk_gpio_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE | RF_SHAREABLE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE },
+ { -1, 0 }
+};
+
+static int mtk_gpio_probe(device_t dev);
+static int mtk_gpio_attach(device_t dev);
+static int mtk_gpio_detach(device_t dev);
+static int mtk_gpio_intr(void *arg);
+
+#define MTK_GPIO_LOCK(sc) mtx_lock_spin(&(sc)->mtx)
+#define MTK_GPIO_UNLOCK(sc) mtx_unlock_spin(&(sc)->mtx)
+#define MTK_GPIO_LOCK_INIT(sc) \
+ mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \
+ "mtk_gpio", MTX_SPIN)
+#define MTK_GPIO_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx)
+
+#define MTK_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val))
+#define MTK_READ_4(sc, reg) bus_read_4((sc)->res[0], (reg))
+
+/* Register definitions */
+#define GPIO_REG(_sc, _reg) ((_reg) + (_sc)->bank_id * 0x4)
+#define GPIO_PIOINT(_sc) GPIO_REG((_sc), 0x0090)
+#define GPIO_PIOEDGE(_sc) GPIO_REG((_sc), 0x00A0)
+#define GPIO_PIORENA(_sc) GPIO_REG((_sc), 0x0050)
+#define GPIO_PIOFENA(_sc) GPIO_REG((_sc), 0x0060)
+#define GPIO_PIODATA(_sc) GPIO_REG((_sc), 0x0020)
+#define GPIO_PIODIR(_sc) GPIO_REG((_sc), 0x0000)
+#define GPIO_PIOPOL(_sc) GPIO_REG((_sc), 0x0010)
+#define GPIO_PIOSET(_sc) GPIO_REG((_sc), 0x0030)
+#define GPIO_PIORESET(_sc) GPIO_REG((_sc), 0x0040)
+
+static struct ofw_compat_data compat_data[] = {
+ { "mtk,mt7621-gpio", 1 },
+ { "mtk,mt7628-gpio", 1 },
+ { NULL, 0 }
+};
+
+static int
+mtk_gpio_probe(device_t dev)
+{
+ phandle_t node;
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ node = ofw_bus_get_node(dev);
+ if (!OF_hasprop(node, "gpio-controller"))
+ return (ENXIO);
+
+ device_set_desc(dev, "MTK GPIO Controller (v2)");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+mtk_pic_register_isrcs(struct mtk_gpio_softc *sc)
+{
+ int error;
+ uint32_t irq;
+ struct intr_irqsrc *isrc;
+ const char *name;
+
+ name = device_get_nameunit(sc->dev);
+ for (irq = 0; irq < sc->num_pins; irq++) {
+ sc->pins[irq].pin_irqsrc.irq = irq;
+ isrc = PIC_INTR_ISRC(sc, irq);
+ error = intr_isrc_register(isrc, sc->dev, 0, "%s", name);
+ if (error != 0) {
+ /* XXX call intr_isrc_deregister */
+ device_printf(sc->dev, "%s failed", __func__);
+ return (error);
+ }
+ }
+
+ return (0);
+}
+
+static int
+mtk_gpio_pin_set_direction(struct mtk_gpio_softc *sc, uint32_t pin,
+ uint32_t dir)
+{
+ uint32_t regval, mask = (1u << pin);
+
+ if (!(sc->pins[pin].pin_caps & dir))
+ return (EINVAL);
+
+ regval = MTK_READ_4(sc, GPIO_PIODIR(sc));
+ if (dir == GPIO_PIN_INPUT)
+ regval &= ~mask;
+ else
+ regval |= mask;
+ MTK_WRITE_4(sc, GPIO_PIODIR(sc), regval);
+
+ sc->pins[pin].pin_flags &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
+ sc->pins[pin].pin_flags |= dir;
+
+ return (0);
+}
+
+static int
+mtk_gpio_pin_set_invert(struct mtk_gpio_softc *sc, uint32_t pin, uint32_t val)
+{
+ uint32_t regval, mask = (1u << pin);
+
+ regval = MTK_READ_4(sc, GPIO_PIOPOL(sc));
+ if (val)
+ regval |= mask;
+ else
+ regval &= ~mask;
+ MTK_WRITE_4(sc, GPIO_PIOPOL(sc), regval);
+ sc->pins[pin].pin_flags &= ~(GPIO_PIN_INVIN | GPIO_PIN_INVOUT);
+ sc->pins[pin].pin_flags |= val;
+
+ return (0);
+}
+
+static void
+mtk_gpio_pin_probe(struct mtk_gpio_softc *sc, uint32_t pin)
+{
+ uint32_t mask = (1u << pin);
+ uint32_t val;
+
+ /* Clear cached gpio config */
+ sc->pins[pin].pin_flags = 0;
+
+ val = MTK_READ_4(sc, GPIO_PIORENA(sc)) |
+ MTK_READ_4(sc, GPIO_PIOFENA(sc));
+ if (val & mask) {
+ /* Pin is in interrupt mode */
+ sc->pins[pin].intr_trigger = INTR_TRIGGER_EDGE;
+ val = MTK_READ_4(sc, GPIO_PIORENA(sc));
+ if (val & mask)
+ sc->pins[pin].intr_polarity = INTR_POLARITY_HIGH;
+ else
+ sc->pins[pin].intr_polarity = INTR_POLARITY_LOW;
+ }
+
+ val = MTK_READ_4(sc, GPIO_PIODIR(sc));
+ if (val & mask)
+ sc->pins[pin].pin_flags |= GPIO_PIN_OUTPUT;
+ else
+ sc->pins[pin].pin_flags |= GPIO_PIN_INPUT;
+
+ val = MTK_READ_4(sc, GPIO_PIOPOL(sc));
+ if (val & mask) {
+ if (sc->pins[pin].pin_flags & GPIO_PIN_INPUT) {
+ sc->pins[pin].pin_flags |= GPIO_PIN_INVIN;
+ } else {
+ sc->pins[pin].pin_flags |= GPIO_PIN_INVOUT;
+ }
+ }
+}
+
+static int
+mtk_gpio_attach(device_t dev)
+{
+ struct mtk_gpio_softc *sc;
+ phandle_t node;
+ uint32_t i, num_pins, bank_id;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ if (bus_alloc_resources(dev, mtk_gpio_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources for device\n");
+ return (ENXIO);
+ }
+
+ MTK_GPIO_LOCK_INIT(sc);
+
+ node = ofw_bus_get_node(dev);
+
+ if (OF_hasprop(node, "clocks"))
+ mtk_soc_start_clock(dev);
+ if (OF_hasprop(node, "resets"))
+ mtk_soc_reset_device(dev);
+
+ if (OF_hasprop(node, "mtk,bank-id") && (OF_getencprop(node,
+ "mtk,bank-id", &bank_id, sizeof(bank_id)) >= 0))
+ sc->bank_id = bank_id;
+ else
+ sc->bank_id = device_get_unit(dev);
+
+ if (OF_hasprop(node, "mtk,num-pins") && (OF_getencprop(node,
+ "mtk,num-pins", &num_pins, sizeof(num_pins)) >= 0))
+ sc->num_pins = num_pins;
+ else
+ sc->num_pins = MTK_GPIO_PINS;
+
+ for (i = 0; i < num_pins; i++) {
+ sc->pins[i].pin_caps |= GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |
+ GPIO_PIN_INVIN | GPIO_PIN_INVOUT;
+ sc->pins[i].intr_polarity = INTR_POLARITY_HIGH;
+ sc->pins[i].intr_trigger = INTR_TRIGGER_EDGE;
+
+ snprintf(sc->pins[i].pin_name, GPIOMAXNAME - 1, "gpio%c%d",
+ device_get_unit(dev) + 'a', i);
+ sc->pins[i].pin_name[GPIOMAXNAME - 1] = '\0';
+
+ mtk_gpio_pin_probe(sc, i);
+ }
+
+ if (mtk_pic_register_isrcs(sc) != 0) {
+ device_printf(dev, "could not register PIC ISRCs\n");
+ goto fail;
+ }
+
+ if (intr_pic_register(dev, OF_xref_from_node(node)) != 0) {
+ device_printf(dev, "could not register PIC\n");
+ goto fail;
+ }
+
+ if (bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE,
+ mtk_gpio_intr, NULL, sc, &sc->intrhand) != 0)
+ goto fail_pic;
+
+ sc->busdev = gpiobus_attach_bus(dev);
+ if (sc->busdev == NULL)
+ goto fail_pic;
+
+ return (0);
+fail_pic:
+ intr_pic_deregister(dev, OF_xref_from_node(node));
+fail:
+ if(sc->intrhand != NULL)
+ bus_teardown_intr(dev, sc->res[1], sc->intrhand);
+ bus_release_resources(dev, mtk_gpio_spec, sc->res);
+ MTK_GPIO_LOCK_DESTROY(sc);
+ return (ENXIO);
+}
+
+static int
+mtk_gpio_detach(device_t dev)
+{
+ struct mtk_gpio_softc *sc = device_get_softc(dev);
+ phandle_t node;
+
+ node = ofw_bus_get_node(dev);
+ intr_pic_deregister(dev, OF_xref_from_node(node));
+ if (sc->intrhand != NULL)
+ bus_teardown_intr(dev, sc->res[1], sc->intrhand);
+ bus_release_resources(dev, mtk_gpio_spec, sc->res);
+ MTK_GPIO_LOCK_DESTROY(sc);
+ return (0);
+}
+
+static device_t
+mtk_gpio_get_bus(device_t dev)
+{
+ struct mtk_gpio_softc *sc = device_get_softc(dev);
+
+ return (sc->busdev);
+}
+
+static int
+mtk_gpio_pin_max(device_t dev, int *maxpin)
+{
+ struct mtk_gpio_softc *sc = device_get_softc(dev);
+
+ *maxpin = sc->num_pins - 1;
+
+ return (0);
+}
+
+static int
+mtk_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+ struct mtk_gpio_softc *sc = device_get_softc(dev);
+
+ if (pin >= sc->num_pins)
+ return (EINVAL);
+
+ MTK_GPIO_LOCK(sc);
+ *caps = sc->pins[pin].pin_caps;
+ MTK_GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+mtk_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
+{
+ struct mtk_gpio_softc *sc = device_get_softc(dev);
+
+ if (pin >= sc->num_pins)
+ return (EINVAL);
+
+ MTK_GPIO_LOCK(sc);
+ *flags = sc->pins[pin].pin_flags;
+ MTK_GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+mtk_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+ struct mtk_gpio_softc *sc = device_get_softc(dev);
+
+ if (pin >= sc->num_pins)
+ return (EINVAL);
+
+ strncpy(name, sc->pins[pin].pin_name, GPIOMAXNAME - 1);
+ name[GPIOMAXNAME - 1] = '\0';
+
+ return (0);
+}
+
+static int
+mtk_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+ struct mtk_gpio_softc *sc;
+ int retval;
+
+ sc = device_get_softc(dev);
+
+ if (pin >= sc->num_pins)
+ return (EINVAL);
+
+ MTK_GPIO_LOCK(sc);
+ retval = mtk_gpio_pin_set_direction(sc, pin,
+ flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT));
+ if (retval == 0)
+ retval = mtk_gpio_pin_set_invert(sc, pin,
+ flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT));
+ MTK_GPIO_UNLOCK(sc);
+
+ return (retval);
+}
+
+static int
+mtk_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
+{
+ struct mtk_gpio_softc *sc;
+ int ret;
+
+ if (pin >= sc->num_pins)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ ret = 0;
+
+ MTK_GPIO_LOCK(sc);
+ if (!(sc->pins[pin].pin_flags & GPIO_PIN_OUTPUT)) {
+ ret = EINVAL;
+ goto out;
+ }
+ if (value)
+ MTK_WRITE_4(sc, GPIO_PIOSET(sc), (1u << pin));
+ else
+ MTK_WRITE_4(sc, GPIO_PIORESET(sc), (1u << pin));
+
+out:
+ MTK_GPIO_UNLOCK(sc);
+
+ return (ret);
+}
+
+static int
+mtk_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
+{
+ struct mtk_gpio_softc *sc;
+ uint32_t data;
+ int ret;
+
+ if (pin >= sc->num_pins)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ ret = 0;
+
+ MTK_GPIO_LOCK(sc);
+ if (!(sc->pins[pin].pin_flags & GPIO_PIN_INPUT)) {
+ ret = EINVAL;
+ goto out;
+ }
+ data = MTK_READ_4(sc, GPIO_PIODATA(sc));
+ *val = (data & (1u << pin)) ? 1 : 0;
+
+out:
+ MTK_GPIO_UNLOCK(sc);
+ return (ret);
+}
+
+static int
+mtk_gpio_pin_toggle(device_t dev, uint32_t pin)
+{
+ struct mtk_gpio_softc *sc;
+ uint32_t val;
+ int ret;
+
+ if (pin >= sc->num_pins)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ ret = 0;
+
+ MTK_GPIO_LOCK(sc);
+ if(!(sc->pins[pin].pin_flags & GPIO_PIN_OUTPUT)) {
+ ret = EINVAL;
+ goto out;
+ }
+ val = MTK_READ_4(sc, GPIO_PIODATA(sc));
+ val &= (1u << pin);
+ if (val)
+ MTK_WRITE_4(sc, GPIO_PIORESET(sc), (1u << pin));
+ else
+ MTK_WRITE_4(sc, GPIO_PIOSET(sc), (1u << pin));
+
+out:
+ MTK_GPIO_UNLOCK(sc);
+
+ return (ret);
+}
+
+static int
+mtk_gpio_pic_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+ struct mtk_gpio_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->num_pins)
+ return (EINVAL);
+
+ *isrcp = PIC_INTR_ISRC(sc, data->fdt.cells[0]);
+ return (0);
+}
+
+static void
+mtk_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct mtk_gpio_softc *sc;
+ struct mtk_gpio_pin_irqsrc *pisrc;
+ uint32_t pin, mask, val;
+
+ sc = device_get_softc(dev);
+
+ pisrc = (struct mtk_gpio_pin_irqsrc *)isrc;
+ pin = pisrc->irq;
+ mask = 1u << pin;
+
+ MTK_GPIO_LOCK(sc);
+
+ if (sc->pins[pin].intr_polarity == INTR_POLARITY_LOW) {
+ val = MTK_READ_4(sc, GPIO_PIORENA(sc)) & ~mask;
+ MTK_WRITE_4(sc, GPIO_PIORENA(sc), val);
+ val = MTK_READ_4(sc, GPIO_PIOFENA(sc)) | mask;
+ MTK_WRITE_4(sc, GPIO_PIOFENA(sc), val);
+ } else {
+ val = MTK_READ_4(sc, GPIO_PIOFENA(sc)) & ~mask;
+ MTK_WRITE_4(sc, GPIO_PIOFENA(sc), val);
+ val = MTK_READ_4(sc, GPIO_PIORENA(sc)) | mask;
+ MTK_WRITE_4(sc, GPIO_PIORENA(sc), val);
+ }
+
+ MTK_GPIO_UNLOCK(sc);
+}
+
+static void
+mtk_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct mtk_gpio_softc *sc;
+ struct mtk_gpio_pin_irqsrc *pisrc;
+ uint32_t pin, mask, val;
+
+ sc = device_get_softc(dev);
+
+ pisrc = (struct mtk_gpio_pin_irqsrc *)isrc;
+ pin = pisrc->irq;
+ mask = 1u << pin;
+
+ MTK_GPIO_LOCK(sc);
+
+ val = MTK_READ_4(sc, GPIO_PIORENA(sc)) & ~mask;
+ MTK_WRITE_4(sc, GPIO_PIORENA(sc), val);
+ val = MTK_READ_4(sc, GPIO_PIOFENA(sc)) & ~mask;
+ MTK_WRITE_4(sc, GPIO_PIOFENA(sc), val);
+
+ MTK_GPIO_UNLOCK(sc);
+}
+
+static void
+mtk_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ mtk_gpio_pic_disable_intr(dev, isrc);
+}
+
+static void
+mtk_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ mtk_gpio_pic_enable_intr(dev, isrc);
+}
+
+static void
+mtk_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct mtk_gpio_softc *sc;
+ struct mtk_gpio_pin_irqsrc *pisrc;
+
+ pisrc = (struct mtk_gpio_pin_irqsrc *)isrc;
+ sc = device_get_softc(dev);
+ MTK_GPIO_LOCK(sc);
+ MTK_WRITE_4(sc, GPIO_PIOINT(sc), 1u << pisrc->irq);
+ MTK_GPIO_UNLOCK(sc);
+}
+
+static int
+mtk_gpio_intr(void *arg)
+{
+ struct mtk_gpio_softc *sc;
+ uint32_t i, interrupts;
+
+ sc = arg;
+ interrupts = MTK_READ_4(sc, GPIO_PIOINT(sc));
+
+ for (i = 0; interrupts != 0; i++, interrupts >>= 1) {
+ if ((interrupts & 0x1) == 0)
+ continue;
+ if (intr_isrc_dispatch(PIC_INTR_ISRC(sc, i),
+ curthread->td_intr_frame) != 0) {
+ device_printf(sc->dev, "spurious interrupt %d\n", i);
+ }
+ }
+
+ return (FILTER_HANDLED);
+}
+
+static phandle_t
+mtk_gpio_get_node(device_t bus, device_t dev)
+{
+
+ /* We only have one child, the GPIO bus, which needs our own node. */
+ return (ofw_bus_get_node(bus));
+}
+
+static device_method_t mtk_gpio_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, mtk_gpio_probe),
+ DEVMETHOD(device_attach, mtk_gpio_attach),
+ DEVMETHOD(device_detach, mtk_gpio_detach),
+
+ /* GPIO protocol */
+ DEVMETHOD(gpio_get_bus, mtk_gpio_get_bus),
+ DEVMETHOD(gpio_pin_max, mtk_gpio_pin_max),
+ DEVMETHOD(gpio_pin_getname, mtk_gpio_pin_getname),
+ DEVMETHOD(gpio_pin_getflags, mtk_gpio_pin_getflags),
+ DEVMETHOD(gpio_pin_getcaps, mtk_gpio_pin_getcaps),
+ DEVMETHOD(gpio_pin_setflags, mtk_gpio_pin_setflags),
+ DEVMETHOD(gpio_pin_get, mtk_gpio_pin_get),
+ DEVMETHOD(gpio_pin_set, mtk_gpio_pin_set),
+ DEVMETHOD(gpio_pin_toggle, mtk_gpio_pin_toggle),
+
+ /* Interrupt controller interface */
+ DEVMETHOD(pic_disable_intr, mtk_gpio_pic_disable_intr),
+ DEVMETHOD(pic_enable_intr, mtk_gpio_pic_enable_intr),
+ DEVMETHOD(pic_map_intr, mtk_gpio_pic_map_intr),
+ DEVMETHOD(pic_post_filter, mtk_gpio_pic_post_filter),
+ DEVMETHOD(pic_post_ithread, mtk_gpio_pic_post_ithread),
+ DEVMETHOD(pic_pre_ithread, mtk_gpio_pic_pre_ithread),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, mtk_gpio_get_node),
+
+ DEVMETHOD_END
+};
+
+static driver_t mtk_gpio_driver = {
+ "gpio",
+ mtk_gpio_methods,
+ sizeof(struct mtk_gpio_softc),
+};
+
+static devclass_t mtk_gpio_devclass;
+
+EARLY_DRIVER_MODULE(mtk_gpio_v2, simplebus, mtk_gpio_driver,
+ mtk_gpio_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
diff --git a/sys/mips/mediatek/mtk_intr_gic.c b/sys/mips/mediatek/mtk_intr_gic.c
new file mode 100644
index 0000000..3eb6ffc
--- /dev/null
+++ b/sys/mips/mediatek/mtk_intr_gic.c
@@ -0,0 +1,377 @@
+/*-
+ * Copyright (c) 2016 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 64 /* We'll only use 64 for now */
+
+#define MTK_INTPOL 0x0100
+#define MTK_INTTRIG 0x0180
+#define MTK_INTDIS 0x0300
+#define MTK_INTENA 0x0380
+#define MTK_INTMASK 0x0400
+#define MTK_INTSTAT 0x0480
+#define MTK_MAPPIN(_i) (0x0500 + (4 * (_i)))
+#define MTK_MAPVPE(_i, _v) (0x2000 + (32 * (_i)) + (((_v) / 32) * 4))
+
+#define MTK_INTPOL_POS 1
+#define MTK_INTPOL_NEG 0
+#define MTK_INTTRIG_EDGE 1
+#define MTK_INTTRIG_LEVEL 0
+#define MTK_PIN_BITS(_i) ((1 << 31) | (_i))
+#define MTK_VPE_BITS(_v) (1 << ((_v) % 32))
+
+static int mtk_gic_intr(void *);
+
+struct mtk_gic_irqsrc {
+ struct intr_irqsrc isrc;
+ u_int irq;
+};
+
+struct mtk_gic_softc {
+ device_t gic_dev;
+ void * gic_intrhand;
+ struct resource * gic_res[2];
+ struct mtk_gic_irqsrc gic_irqs[MTK_NIRQS];
+ struct mtx mutex;
+ uint32_t nirqs;
+};
+
+#define GIC_INTR_ISRC(sc, irq) (&(sc)->gic_irqs[(irq)].isrc)
+
+static struct resource_spec mtk_gic_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Registers */
+ { SYS_RES_IRQ, 0, RF_ACTIVE }, /* Parent interrupt 1 */
+ { -1, 0 }
+};
+
+static struct ofw_compat_data compat_data[] = {
+ { "mti,gic", 1 },
+ { NULL, 0 }
+};
+
+#if 0
+#define READ4(_sc, _reg) \
+ bus_space_read_4((_sc)->bst, (_sc)->bsh, _reg)
+#define WRITE4(_sc, _reg, _val) \
+ bus_space_write_4((_sc)->bst, (_sc)->bsh, _reg, _val)
+#else
+#define READ4(_sc, _reg) bus_read_4((_sc)->gic_res[0], (_reg))
+#define WRITE4(_sc, _reg, _val) bus_write_4((_sc)->gic_res[0], (_reg), (_val))
+#endif
+
+static int
+mtk_gic_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 (GIC)");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static inline void
+gic_irq_unmask(struct mtk_gic_softc *sc, u_int irq)
+{
+
+ WRITE4(sc, MTK_INTENA, (1u << (irq)));
+}
+
+static inline void
+gic_irq_mask(struct mtk_gic_softc *sc, u_int irq)
+{
+
+ WRITE4(sc, MTK_INTDIS, (1u << (irq)));
+}
+
+static inline intptr_t
+gic_xref(device_t dev)
+{
+
+ return (OF_xref_from_node(ofw_bus_get_node(dev)));
+}
+
+static int
+mtk_gic_register_isrcs(struct mtk_gic_softc *sc)
+{
+ int error;
+ uint32_t irq;
+ struct intr_irqsrc *isrc;
+ const char *name;
+
+ name = device_get_nameunit(sc->gic_dev);
+ for (irq = 0; irq < sc->nirqs; irq++) {
+ sc->gic_irqs[irq].irq = irq;
+ isrc = GIC_INTR_ISRC(sc, irq);
+ error = intr_isrc_register(isrc, sc->gic_dev, 0, "%s", name);
+ if (error != 0) {
+ /* XXX call intr_isrc_deregister */
+ device_printf(sc->gic_dev, "%s failed", __func__);
+ return (error);
+ }
+ }
+
+ return (0);
+}
+
+static int
+mtk_gic_attach(device_t dev)
+{
+ struct mtk_gic_softc *sc;
+ intptr_t xref = gic_xref(dev);
+ int i;
+
+ sc = device_get_softc(dev);
+
+ if (bus_alloc_resources(dev, mtk_gic_spec, sc->gic_res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ sc->gic_dev = dev;
+
+ /* Initialize mutex */
+ mtx_init(&sc->mutex, "PIC lock", "", MTX_SPIN);
+
+ /* Set the number of interrupts */
+ sc->nirqs = nitems(sc->gic_irqs);
+
+ /* Mask all interrupts */
+ WRITE4(sc, MTK_INTDIS, 0xFFFFFFFF);
+
+ /* All interrupts are of type level */
+ WRITE4(sc, MTK_INTTRIG, 0x00000000);
+
+ /* All interrupts are of positive polarity */
+ WRITE4(sc, MTK_INTPOL, 0xFFFFFFFF);
+
+ /*
+ * Route all interrupts to pin 0 on VPE 0;
+ */
+ for (i = 0; i < 32; i++) {
+ WRITE4(sc, MTK_MAPPIN(i), MTK_PIN_BITS(0));
+ WRITE4(sc, MTK_MAPVPE(i, 0), MTK_VPE_BITS(0));
+ }
+
+ /* Register the interrupts */
+ if (mtk_gic_register_isrcs(sc) != 0) {
+ device_printf(dev, "could not register GIC 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->gic_res[1], INTR_TYPE_CLK,
+ mtk_gic_intr, NULL, sc, &sc->gic_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_gic_spec, sc->gic_res);
+ return(ENXIO);
+}
+
+static int
+mtk_gic_intr(void *arg)
+{
+ struct mtk_gic_softc *sc = arg;
+ struct thread *td;
+ uint32_t i, intr;
+
+ td = curthread;
+ /* Workaround: do not inflate intr nesting level */
+ td->td_intr_nesting_level--;
+
+ intr = READ4(sc, MTK_INTSTAT) & READ4(sc, MTK_INTMASK);
+ while ((i = fls(intr)) != 0) {
+ i--;
+ intr &= ~(1u << i);
+
+ if (intr_isrc_dispatch(GIC_INTR_ISRC(sc, i),
+ curthread->td_intr_frame) != 0) {
+ device_printf(sc->gic_dev,
+ "Stray interrupt %u detected\n", i);
+ gic_irq_mask(sc, i);
+ continue;
+ }
+ }
+
+ KASSERT(i == 0, ("all interrupts handled"));
+
+ td->td_intr_nesting_level++;
+
+ return (FILTER_HANDLED);
+}
+
+static int
+mtk_gic_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+#ifdef FDT
+ struct mtk_gic_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 = GIC_INTR_ISRC(sc, data->fdt.cells[0]);
+ return (0);
+#else
+ return (EINVAL);
+#endif
+}
+
+static void
+mtk_gic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ u_int irq;
+
+ irq = ((struct mtk_gic_irqsrc *)isrc)->irq;
+ gic_irq_unmask(device_get_softc(dev), irq);
+}
+
+static void
+mtk_gic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ u_int irq;
+
+ irq = ((struct mtk_gic_irqsrc *)isrc)->irq;
+ gic_irq_mask(device_get_softc(dev), irq);
+}
+
+static void
+mtk_gic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ mtk_gic_disable_intr(dev, isrc);
+}
+
+static void
+mtk_gic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ mtk_gic_enable_intr(dev, isrc);
+}
+
+static void
+mtk_gic_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+}
+
+#ifdef SMP
+static int
+mtk_gic_bind(device_t dev, struct intr_irqsrc *isrc)
+{
+ return (EOPNOTSUPP);
+}
+
+static void
+mtk_gic_init_secondary(device_t dev)
+{
+}
+
+static void
+mtk_gic_ipi_send(device_t dev, struct intr_irqsrc *isrc, cpuset_t cpus)
+{
+}
+#endif
+
+static device_method_t mtk_gic_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, mtk_gic_probe),
+ DEVMETHOD(device_attach, mtk_gic_attach),
+ /* Interrupt controller interface */
+ DEVMETHOD(pic_disable_intr, mtk_gic_disable_intr),
+ DEVMETHOD(pic_enable_intr, mtk_gic_enable_intr),
+ DEVMETHOD(pic_map_intr, mtk_gic_map_intr),
+ DEVMETHOD(pic_post_filter, mtk_gic_post_filter),
+ DEVMETHOD(pic_post_ithread, mtk_gic_post_ithread),
+ DEVMETHOD(pic_pre_ithread, mtk_gic_pre_ithread),
+#ifdef SMP
+ DEVMETHOD(pic_bind, mtk_gic_bind),
+ DEVMETHOD(pic_init_secondary, mtk_gic_init_secondary),
+ DEVMETHOD(pic_ipi_send, mtk_gic_ipi_send),
+#endif
+ { 0, 0 }
+};
+
+static driver_t mtk_gic_driver = {
+ "intc",
+ mtk_gic_methods,
+ sizeof(struct mtk_gic_softc),
+};
+
+static devclass_t mtk_gic_devclass;
+
+EARLY_DRIVER_MODULE(intc_gic, simplebus, mtk_gic_driver, mtk_gic_devclass, 0, 0,
+ BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
diff --git a/sys/mips/mediatek/mtk_intr_v1.c b/sys/mips/mediatek/mtk_intr_v1.c
new file mode 100644
index 0000000..f1ae3a2
--- /dev/null
+++ b/sys/mips/mediatek/mtk_intr_v1.c
@@ -0,0 +1,353 @@
+/*-
+ * 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 0x0000
+#define MTK_IRQ1STAT 0x0004
+#define MTK_INTTYPE 0x0020
+#define MTK_INTRAW 0x0030
+#define MTK_INTENA 0x0034
+#define MTK_INTDIS 0x0038
+
+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,rt2880-intc", 1 },
+ { "ralink,rt3050-intc", 1 },
+ { "ralink,rt3352-intc", 1 },
+ { "ralink,rt3883-intc", 1 },
+ { "ralink,rt5350-intc", 1 },
+ { "ralink,mt7620a-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, 0x7FFFFFFF);
+
+ /* But enable interrupt generation/masking */
+ WRITE4(sc, MTK_INTENA, 0x80000000);
+
+ /* Set all interrupts to type 0 */
+ WRITE4(sc, MTK_INTTYPE, 0x00000000);
+
+ /* 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_v1, simplebus, mtk_pic_driver, mtk_pic_devclass, 0, 0,
+ BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
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);
diff --git a/sys/mips/mediatek/mtk_machdep.c b/sys/mips/mediatek/mtk_machdep.c
new file mode 100644
index 0000000..eeadd4f
--- /dev/null
+++ b/sys/mips/mediatek/mtk_machdep.c
@@ -0,0 +1,286 @@
+/*-
+ * Copyright (C) 2015-2016 by Stanislav Galabov. All rights reserved.
+ * Copyright (C) 2010-2011 by Aleksandr Rybalko. All rights reserved.
+ * Copyright (C) 2007 by Oleksandr Tymoshenko. 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 HIS RELATIVES 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 MIND, 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_ddb.h"
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/imgact.h>
+#include <sys/bio.h>
+#include <sys/buf.h>
+#include <sys/bus.h>
+#include <sys/cpu.h>
+#include <sys/cons.h>
+#include <sys/exec.h>
+#include <sys/ucontext.h>
+#include <sys/proc.h>
+#include <sys/kdb.h>
+#include <sys/ptrace.h>
+#include <sys/reboot.h>
+#include <sys/signalvar.h>
+#include <sys/sysent.h>
+#include <sys/sysproto.h>
+#include <sys/user.h>
+
+#include <vm/vm.h>
+#include <vm/vm_object.h>
+#include <vm/vm_page.h>
+
+#include <machine/cache.h>
+#include <machine/clock.h>
+#include <machine/cpu.h>
+#include <machine/cpuinfo.h>
+#include <machine/cpufunc.h>
+#include <machine/cpuregs.h>
+#include <machine/hwfunc.h>
+#include <machine/intr_machdep.h>
+#include <machine/locore.h>
+#include <machine/md_var.h>
+#include <machine/pte.h>
+#include <machine/sigframe.h>
+#include <machine/trap.h>
+#include <machine/vmparam.h>
+
+#include <mips/mediatek/mtk_sysctl.h>
+#include <mips/mediatek/mtk_soc.h>
+
+#include "opt_platform.h"
+#include "opt_rt305x.h"
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+
+extern int *edata;
+extern int *end;
+static char boot1_env[0x1000];
+
+void
+platform_cpu_init()
+{
+ /* Nothing special */
+}
+
+static void
+mips_init(void)
+{
+ struct mem_region mr[FDT_MEM_REGIONS];
+ uint64_t val;
+ int i, j, mr_cnt;
+ char *memsize;
+
+ printf("entry: mips_init()\n");
+
+ bootverbose = 1;
+
+ for (i = 0; i < 10; i++)
+ phys_avail[i] = 0;
+
+ dump_avail[0] = phys_avail[0] = MIPS_KSEG0_TO_PHYS(kernel_kseg0_end);
+
+ /*
+ * The most low memory MT7621 can have. Currently MT7621 is the chip
+ * that supports the most memory, so that seems reasonable.
+ */
+ realmem = btoc(448 * 1024 * 1024);
+
+ if (fdt_get_mem_regions(mr, &mr_cnt, &val) == 0) {
+ physmem = btoc(val);
+
+ printf("RAM size: %ldMB (from FDT)\n",
+ ctob(physmem) / (1024 * 1024));
+
+ KASSERT((phys_avail[0] >= mr[0].mr_start) && \
+ (phys_avail[0] < (mr[0].mr_start + mr[0].mr_size)),
+ ("First region is not within FDT memory range"));
+
+ /* Limit size of the first region */
+ phys_avail[1] = (mr[0].mr_start +
+ MIN(mr[0].mr_size, ctob(realmem)));
+ dump_avail[1] = phys_avail[1];
+
+ /* Add the rest of the regions */
+ for (i = 1, j = 2; i < mr_cnt; i++, j+=2) {
+ phys_avail[j] = mr[i].mr_start;
+ phys_avail[j+1] = (mr[i].mr_start + mr[i].mr_size);
+ dump_avail[j] = phys_avail[j];
+ dump_avail[j+1] = phys_avail[j+1];
+ }
+ } else {
+ if ((memsize = kern_getenv("memsize")) != NULL) {
+ physmem = btoc(strtol(memsize, NULL, 0) << 20);
+ printf("RAM size: %ldMB (from memsize)\n",
+ ctob(physmem) / (1024 * 1024));
+ } else { /* All else failed, assume 32MB */
+ physmem = btoc(32 * 1024 * 1024);
+ printf("RAM size: %ldMB (assumed)\n",
+ ctob(physmem) / (1024 * 1024));
+ }
+
+ if (ctob(physmem) < (448 * 1024 * 1024)) {
+ /*
+ * Anything up to 448MB is assumed to be directly
+ * mappable as low memory...
+ */
+ dump_avail[1] = phys_avail[1] = ctob(physmem);
+ } else if (mtk_soc_get_socid() == MTK_SOC_MT7621) {
+ /*
+ * On MT7621 the low memory is limited to 448MB, the
+ * rest is high memory, mapped at 0x20000000
+ */
+ phys_avail[1] = 448 * 1024 * 1024;
+ phys_avail[2] = 0x20000000;
+ phys_avail[3] = phys_avail[2] + ctob(physmem) -
+ phys_avail[1];
+ dump_avail[1] = phys_avail[1] - phys_avail[0];
+ dump_avail[2] = phys_avail[2];
+ dump_avail[3] = phys_avail[3] - phys_avail[2];
+ } else {
+ /*
+ * We have > 448MB RAM and we're not MT7621? Currently
+ * there is no such chip, so we'll just limit the RAM to
+ * 32MB and let the user know...
+ */
+ printf("Unknown chip, assuming 32MB RAM\n");
+ physmem = btoc(32 * 1024 * 1024);
+ dump_avail[1] = phys_avail[1] = ctob(physmem);
+ }
+ }
+
+ if (physmem < realmem)
+ realmem = physmem;
+
+ init_param1();
+ init_param2(physmem);
+ mips_cpu_init();
+ pmap_bootstrap();
+ mips_proc0_init();
+ mutex_init();
+ kdb_init();
+#ifdef KDB
+ if (boothowto & RB_KDB)
+ kdb_enter(KDB_WHY_BOOTFLAGS, "Boot flags requested debugger");
+#endif
+}
+
+void
+platform_reset(void)
+{
+
+ mtk_soc_reset();
+}
+
+void
+platform_start(__register_t a0 __unused, __register_t a1 __unused,
+ __register_t a2 __unused, __register_t a3 __unused)
+{
+ vm_offset_t kernend;
+ int argc = a0, i;//, res;
+ uint32_t timer_clk;
+ char **argv = (char **)MIPS_PHYS_TO_KSEG0(a1);
+ char **envp = (char **)MIPS_PHYS_TO_KSEG0(a2);
+ void *dtbp;
+
+ /* clear the BSS and SBSS segments */
+ kernend = (vm_offset_t)&end;
+ memset(&edata, 0, kernend - (vm_offset_t)(&edata));
+
+ mips_postboot_fixup();
+
+ /* Initialize pcpu stuff */
+ mips_pcpu0_init();
+
+ dtbp = &fdt_static_dtb;
+ if (OF_install(OFW_FDT, 0) == FALSE)
+ while (1);
+ if (OF_init((void *)dtbp) != 0)
+ while (1);
+
+ mtk_soc_try_early_detect();
+ if ((timer_clk = mtk_soc_get_timerclk()) == 0)
+ timer_clk = 1000000000; /* no such speed yet */
+
+ mips_timer_early_init(timer_clk);
+
+ /* initialize console so that we have printf */
+ boothowto |= (RB_SERIAL | RB_MULTIPLE); /* Use multiple consoles */
+ boothowto |= (RB_VERBOSE);
+ cninit();
+
+ init_static_kenv(boot1_env, sizeof(boot1_env));
+
+ printf("FDT DTB at: 0x%08x\n", (uint32_t)dtbp);
+
+ printf("CPU clock: %4dMHz\n", mtk_soc_get_cpuclk()/(1000*1000));
+ printf("Timer clock: %4dMHz\n", timer_clk/(1000*1000));
+ printf("UART clock: %4dMHz\n\n", mtk_soc_get_uartclk()/(1000*1000));
+
+ printf("U-Boot args (from %d args):\n", argc - 1);
+
+ if (argc == 1)
+ printf("\tNone\n");
+
+ for (i = 1; i < argc; i++) {
+ char *n = "argv ", *arg;
+
+ if (i > 99)
+ break;
+
+ if (argv[i])
+ {
+ arg = (char *)(intptr_t)MIPS_PHYS_TO_KSEG0(argv[i]);
+ printf("\targv[%d] = %s\n", i, arg);
+ sprintf(n, "argv%d", i);
+ kern_setenv(n, arg);
+ }
+ }
+
+ printf("Environment:\n");
+
+ for (i = 0; envp[i] && MIPS_IS_VALID_PTR(envp[i]); i++) {
+ char *n, *arg;
+
+ arg = (char *)(intptr_t)MIPS_PHYS_TO_KSEG0(envp[i]);
+ if (! MIPS_IS_VALID_PTR(arg))
+ continue;
+ printf("\t%s\n", arg);
+ n = strsep(&arg, "=");
+ if (arg == NULL)
+ kern_setenv(n, "1");
+ else
+ kern_setenv(n, arg);
+ }
+
+
+ mips_init();
+ mips_timer_init_params(timer_clk, 0);
+}
diff --git a/sys/mips/mediatek/mtk_ohci.c b/sys/mips/mediatek/mtk_ohci.c
new file mode 100644
index 0000000..91c98ce
--- /dev/null
+++ b/sys/mips/mediatek/mtk_ohci.c
@@ -0,0 +1,223 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2015 Stanislav Galabov. All rights reserved.
+ * Copyright (c) 2010,2011 Aleksandr Rybalko. All rights reserved.
+ * Copyright (c) 2007-2008 Hans Petter Selasky. 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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 <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+#include <sys/rman.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+
+#include <dev/usb/controller/ohci.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#define OHCI_HC_DEVSTR "MTK USB Controller"
+
+static device_probe_t ohci_fdt_probe;
+static device_attach_t ohci_fdt_attach;
+static device_detach_t ohci_fdt_detach;
+
+static int
+ohci_fdt_probe(device_t self)
+{
+
+ if (!ofw_bus_status_okay(self))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(self, "ralink,rt3xxx-ohci"))
+ return (ENXIO);
+
+ device_set_desc(self, OHCI_HC_DEVSTR);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ohci_fdt_attach(device_t self)
+{
+ ohci_softc_t *sc = device_get_softc(self);
+ int err;
+ int rid;
+
+ /* initialise some bus fields */
+ sc->sc_bus.parent = self;
+ sc->sc_bus.devices = sc->sc_devices;
+ sc->sc_bus.devices_max = OHCI_MAX_DEVICES;
+ sc->sc_bus.dma_bits = 32;
+
+ /* get all DMA memory */
+ if (usb_bus_mem_alloc_all(&sc->sc_bus,
+ USB_GET_DMA_TAG(self), &ohci_iterate_hw_softc)) {
+ printf("No mem\n");
+ return (ENOMEM);
+ }
+
+ rid = 0;
+ sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_io_res) {
+ device_printf(self, "Could not map memory\n");
+ goto error;
+ }
+ sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
+ sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
+ sc->sc_io_size = rman_get_size(sc->sc_io_res);
+
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->sc_irq_res == NULL) {
+ device_printf(self, "Could not allocate irq\n");
+ goto error;
+ }
+
+ sc->sc_bus.bdev = device_add_child(self, "usbus", -1);
+ if (!(sc->sc_bus.bdev)) {
+ device_printf(self, "Could not add USB device\n");
+ goto error;
+ }
+ device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+ device_set_desc(sc->sc_bus.bdev, OHCI_HC_DEVSTR);
+
+ sprintf(sc->sc_vendor, "MediaTek");
+
+ err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, (driver_intr_t *)ohci_interrupt, sc, &sc->sc_intr_hdl);
+ if (err) {
+ device_printf(self, "Could not setup irq, %d\n", err);
+ sc->sc_intr_hdl = NULL;
+ goto error;
+ }
+
+ err = ohci_init(sc);
+ if (!err) {
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+ }
+ if (err) {
+ device_printf(self, "USB init failed err=%d\n", err);
+ goto error;
+ }
+ return (0);
+
+error:
+ ohci_fdt_detach(self);
+ return (ENXIO);
+}
+
+static int
+ohci_fdt_detach(device_t self)
+{
+ ohci_softc_t *sc = device_get_softc(self);
+ device_t bdev;
+ int err;
+
+ if (sc->sc_bus.bdev) {
+ bdev = sc->sc_bus.bdev;
+ device_detach(bdev);
+ device_delete_child(self, bdev);
+ }
+ /* during module unload there are lots of children leftover */
+ device_delete_children(self);
+
+ if (sc->sc_irq_res && sc->sc_intr_hdl) {
+ /*
+ * only call ohci_detach() after ohci_init()
+ */
+ ohci_detach(sc);
+
+ err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl);
+ if (err)
+ device_printf(self, "Could not tear down irq, %d\n",
+ err);
+ sc->sc_intr_hdl = NULL;
+ }
+ if (sc->sc_irq_res) {
+ bus_release_resource(self, SYS_RES_IRQ, 0,
+ sc->sc_irq_res);
+ sc->sc_irq_res = NULL;
+ }
+ if (sc->sc_io_res) {
+ bus_release_resource(self, SYS_RES_MEMORY, 0,
+ sc->sc_io_res);
+ sc->sc_io_res = NULL;
+ }
+ usb_bus_mem_free_all(&sc->sc_bus, &ohci_iterate_hw_softc);
+
+ return (0);
+}
+
+static device_method_t ohci_fdt_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ohci_fdt_probe),
+ DEVMETHOD(device_attach, ohci_fdt_attach),
+ DEVMETHOD(device_detach, ohci_fdt_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ DEVMETHOD_END
+};
+
+static driver_t ohci_fdt_driver = {
+ .name = "ohci",
+ .methods = ohci_fdt_methods,
+ .size = sizeof(ohci_softc_t),
+};
+
+static devclass_t ohci_fdt_devclass;
+
+DRIVER_MODULE(ohci, simplebus, ohci_fdt_driver, ohci_fdt_devclass, 0, 0);
diff --git a/sys/mips/mediatek/mtk_pcie.c b/sys/mips/mediatek/mtk_pcie.c
new file mode 100644
index 0000000..a43d277
--- /dev/null
+++ b/sys/mips/mediatek/mtk_pcie.c
@@ -0,0 +1,1489 @@
+/*-
+ * Copyright (c) 2016 Stanislav Galabov.
+ *
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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.
+ *
+ * The pci allocator parts are based on code from sys/dev/arm/mv/:
+ *
+ * Copyright (c) 2008 MARVELL INTERNATIONAL LTD.
+ * Copyright (c) 2010 The FreeBSD Foundation
+ * Copyright (c) 2010-2012 Semihalf
+ * All rights reserved.
+ *
+ * Developed by Semihalf.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <sys/bus.h>
+#include <sys/interrupt.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/endian.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <vm/vm_extern.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+#include <machine/pmap.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+
+#include <dev/pci/pcib_private.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/fdt/fdt_clock.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <mips/mediatek/mtk_pcie.h>
+#include <mips/mediatek/mtk_soc.h>
+#include <mips/mediatek/mtk_sysctl.h>
+#include <mips/mediatek/fdt_reset.h>
+
+#include "pcib_if.h"
+#include "pic_if.h"
+
+/*
+ * Note: We only support PCIe at the moment.
+ * Most SoCs in the Ralink/Mediatek family that we target actually don't
+ * support PCI anyway, with the notable exceptions being RT3662/RT3883, which
+ * support both PCI and PCIe. If there exists a board based on one of them
+ * which is of interest in the future it shouldn't be too hard to enable PCI
+ * support for it.
+ */
+
+/* Chip specific function declarations */
+static int mtk_pcie_phy_init(device_t);
+static int mtk_pcie_phy_start(device_t);
+static int mtk_pcie_phy_stop(device_t);
+static int mtk_pcie_phy_mt7621_init(device_t);
+static int mtk_pcie_phy_mt7628_init(device_t);
+static int mtk_pcie_phy_mt7620_init(device_t);
+static int mtk_pcie_phy_rt3883_init(device_t);
+static void mtk_pcie_phy_setup_slots(device_t);
+
+/* Generic declarations */
+struct mtx mtk_pci_mtx;
+MTX_SYSINIT(mtk_pci_mtx, &mtk_pci_mtx, "MTK PCIe mutex", MTX_SPIN);
+
+static int mtk_pcib_init(device_t, int, int);
+static int mtk_pci_intr(void *);
+
+static struct mtk_pci_softc *mt_sc = NULL;
+
+struct mtk_pci_range {
+ u_long base;
+ u_long len;
+};
+
+#define FDT_RANGES_CELLS (3 * 2)
+
+static void
+mtk_pci_range_dump(struct mtk_pci_range *range)
+{
+#ifdef DEBUG
+ printf("\n");
+ printf(" base = 0x%08lx\n", range->base);
+ printf(" len = 0x%08lx\n", range->len);
+#endif
+}
+
+static int
+mtk_pci_ranges_decode(phandle_t node, struct mtk_pci_range *io_space,
+ struct mtk_pci_range *mem_space)
+{
+ struct mtk_pci_range *pci_space;
+ pcell_t ranges[FDT_RANGES_CELLS];
+ pcell_t *rangesptr;
+ pcell_t cell0, cell1, cell2;
+ int tuples, i, rv, len;
+
+ /*
+ * Retrieve 'ranges' property.
+ */
+ if (!OF_hasprop(node, "ranges")) {
+ printf("%s: %d\n", __FUNCTION__, 1);
+ return (EINVAL);
+ }
+
+ len = OF_getproplen(node, "ranges");
+ if (len > sizeof(ranges)) {
+ printf("%s: %d\n", __FUNCTION__, 2);
+ return (ENOMEM);
+ }
+
+ if (OF_getprop(node, "ranges", ranges, sizeof(ranges)) <= 0) {
+ printf("%s: %d\n", __FUNCTION__, 3);
+ return (EINVAL);
+ }
+
+ tuples = len / (sizeof(pcell_t) * 3);
+
+ /*
+ * Initialize the ranges so that we don't have to worry about
+ * having them all defined in the FDT. In particular, it is
+ * perfectly fine not to want I/O space on PCI busses.
+ */
+ bzero(io_space, sizeof(*io_space));
+ bzero(mem_space, sizeof(*mem_space));
+
+ rangesptr = &ranges[0];
+ for (i = 0; i < tuples; i++) {
+ cell0 = fdt_data_get((void *)rangesptr, 1);
+ rangesptr++;
+ cell1 = fdt_data_get((void *)rangesptr, 1);
+ rangesptr++;
+ cell2 = fdt_data_get((void *)rangesptr, 1);
+ rangesptr++;
+
+ if (cell0 == 2) {
+ pci_space = mem_space;
+ } else if (cell0 == 1) {
+ pci_space = io_space;
+ } else {
+ rv = ERANGE;
+ printf("%s: %d\n", __FUNCTION__, 4);
+ goto out;
+ }
+
+ pci_space->base = cell1;
+ pci_space->len = cell2;
+ }
+
+ rv = 0;
+out:
+ return (rv);
+}
+
+static int
+mtk_pci_ranges(phandle_t node, struct mtk_pci_range *io_space,
+ struct mtk_pci_range *mem_space)
+{
+ int err;
+
+ if ((err = mtk_pci_ranges_decode(node, io_space, mem_space)) != 0) {
+ return (err);
+ }
+
+ mtk_pci_range_dump(io_space);
+ mtk_pci_range_dump(mem_space);
+
+ return (0);
+}
+
+static struct ofw_compat_data compat_data[] = {
+ { "ralink,rt3662-pcie", MTK_SOC_RT3883 },
+ { "ralink,rt3883-pcie", MTK_SOC_RT3883 },
+ { "ralink,mt7620a-pcie", MTK_SOC_MT7620A },
+ { "ralink,mt7621-pcie", MTK_SOC_MT7621 },
+ { "ralink,mt7628-pcie", MTK_SOC_MT7628 },
+ { "ralink,mt7688-pcie", MTK_SOC_MT7628 },
+ { NULL, MTK_SOC_UNKNOWN }
+};
+
+static int
+mtk_pci_probe(device_t dev)
+{
+ struct mtk_pci_softc *sc = device_get_softc(dev);
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ sc->socid = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+ if (sc->socid == MTK_SOC_UNKNOWN)
+ return (ENXIO);
+
+ device_set_desc(dev, "MTK PCIe Controller");
+
+ return (0);
+}
+
+static int
+mtk_pci_attach(device_t dev)
+{
+ struct mtk_pci_softc *sc = device_get_softc(dev);
+ struct mtk_pci_range io_space, mem_space;
+ phandle_t node;
+ intptr_t xref;
+ int i, rid;
+
+ sc->sc_dev = dev;
+ mt_sc = sc;
+ sc->addr_mask = 0xffffffff;
+
+ /* Request our memory */
+ rid = 0;
+ sc->pci_res[0] = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->pci_res[0] == NULL) {
+ device_printf(dev, "could not allocate memory resource\n");
+ return (ENXIO);
+ }
+
+ /* See how many interrupts we need */
+ if (sc->socid == MTK_SOC_MT7621)
+ sc->sc_num_irq = 3;
+ else {
+ sc->sc_num_irq = 1;
+ sc->pci_res[2] = sc->pci_res[3] = NULL;
+ sc->pci_intrhand[1] = sc->pci_intrhand[2] = NULL;
+ }
+
+ /* Request our interrupts */
+ for (i = 1; i <= sc->sc_num_irq ; i++) {
+ rid = i - 1;
+ sc->pci_res[i] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (sc->pci_res[i] == NULL) {
+ device_printf(dev, "could not allocate interrupt "
+ "resource %d\n", rid);
+ goto cleanup_res;
+ }
+ }
+
+ /* Parse our PCI 'ranges' property */
+ node = ofw_bus_get_node(dev);
+ xref = OF_xref_from_node(node);
+ if (mtk_pci_ranges(node, &io_space, &mem_space)) {
+ device_printf(dev, "could not retrieve 'ranges' data\n");
+ goto cleanup_res;
+ }
+
+ /* Memory, I/O and IRQ resource limits */
+ sc->sc_io_base = io_space.base;
+ sc->sc_io_size = io_space.len;
+ sc->sc_mem_base = mem_space.base;
+ sc->sc_mem_size = mem_space.len;
+ sc->sc_irq_start = MTK_PCIE0_IRQ;
+ sc->sc_irq_end = MTK_PCIE2_IRQ;
+
+ /* Init resource managers for memory, I/O and IRQ */
+ sc->sc_mem_rman.rm_type = RMAN_ARRAY;
+ sc->sc_mem_rman.rm_descr = "mtk pcie memory window";
+ if (rman_init(&sc->sc_mem_rman) != 0 ||
+ rman_manage_region(&sc->sc_mem_rman, sc->sc_mem_base,
+ sc->sc_mem_base + sc->sc_mem_size - 1) != 0) {
+ device_printf(dev, "failed to setup memory rman\n");
+ goto cleanup_res;
+ }
+
+ sc->sc_io_rman.rm_type = RMAN_ARRAY;
+ sc->sc_io_rman.rm_descr = "mtk pcie io window";
+ if (rman_init(&sc->sc_io_rman) != 0 ||
+ rman_manage_region(&sc->sc_io_rman, sc->sc_io_base,
+ sc->sc_io_base + sc->sc_io_size - 1) != 0) {
+ device_printf(dev, "failed to setup io rman\n");
+ goto cleanup_res;
+ }
+
+ sc->sc_irq_rman.rm_type = RMAN_ARRAY;
+ sc->sc_irq_rman.rm_descr = "mtk pcie irqs";
+ if (rman_init(&sc->sc_irq_rman) != 0 ||
+ rman_manage_region(&sc->sc_irq_rman, sc->sc_irq_start,
+ sc->sc_irq_end) != 0) {
+ device_printf(dev, "failed to setup irq rman\n");
+ goto cleanup_res;
+ }
+
+ /* Do SoC-specific PCIe initialization */
+ if (mtk_pcie_phy_init(dev)) {
+ device_printf(dev, "pcie phy init failed\n");
+ goto cleanup_rman;
+ }
+
+ /* Register ourselves as an interrupt controller */
+ if (intr_pic_register(dev, xref) != 0) {
+ device_printf(dev, "could not register PIC\n");
+ goto cleanup_rman;
+ }
+
+ /* Set up our interrupt handler */
+ for (i = 1; i <= sc->sc_num_irq; i++) {
+ sc->pci_intrhand[i - 1] = NULL;
+ if (bus_setup_intr(dev, sc->pci_res[i], INTR_TYPE_MISC,
+ mtk_pci_intr, NULL, sc, &sc->pci_intrhand[i - 1])) {
+ device_printf(dev, "could not setup intr handler %d\n",
+ i);
+ goto cleanup;
+ }
+ }
+
+ /* Do generic PCIe initialization and resource allocation */
+ mtk_pcib_init(dev, 0, PCI_SLOTMAX);
+
+ /* Attach our PCI child so bus enumeration can start */
+ if (device_add_child(dev, "pci", -1) == NULL) {
+ device_printf(dev, "could not attach pci bus\n");
+ goto cleanup;
+ }
+
+ /* And finally, attach ourselves to the bus */
+ if (bus_generic_attach(dev)) {
+ device_printf(dev, "could not attach to bus\n");
+ goto cleanup;
+ }
+
+ return (0);
+
+cleanup:
+#ifdef notyet
+ intr_pic_unregister(dev, xref);
+#endif
+ for (i = 1; i <= sc->sc_num_irq; i++) {
+ if (sc->pci_intrhand[i - 1] != NULL)
+ bus_teardown_intr(dev, sc->pci_res[i],
+ sc->pci_intrhand[i - 1]);
+ }
+cleanup_rman:
+ mtk_pcie_phy_stop(dev);
+ rman_fini(&sc->sc_irq_rman);
+ rman_fini(&sc->sc_io_rman);
+ rman_fini(&sc->sc_mem_rman);
+cleanup_res:
+ mt_sc = NULL;
+ if (sc->pci_res[0] != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->pci_res[0]);
+ if (sc->pci_res[1] != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pci_res[1]);
+ if (sc->pci_res[2] != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 1, sc->pci_res[2]);
+ if (sc->pci_res[3] != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 2, sc->pci_res[3]);
+ return (ENXIO);
+}
+
+static int
+mtk_pci_read_ivar(device_t dev, device_t child, int which,
+ uintptr_t *result)
+{
+ struct mtk_pci_softc *sc = device_get_softc(dev);
+
+ switch (which) {
+ case PCIB_IVAR_DOMAIN:
+ *result = device_get_unit(dev);
+ return (0);
+ case PCIB_IVAR_BUS:
+ *result = sc->sc_busno;
+ return (0);
+ }
+
+ return (ENOENT);
+}
+
+static int
+mtk_pci_write_ivar(device_t dev, device_t child, int which,
+ uintptr_t result)
+{
+ struct mtk_pci_softc *sc = device_get_softc(dev);
+
+ switch (which) {
+ case PCIB_IVAR_BUS:
+ sc->sc_busno = result;
+ return (0);
+ }
+
+ return (ENOENT);
+}
+
+static struct resource *
+mtk_pci_alloc_resource(device_t bus, device_t child, int type, int *rid,
+ rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
+{
+ struct mtk_pci_softc *sc = device_get_softc(bus);
+ struct resource *rv;
+ struct rman *rm;
+
+ switch (type) {
+ case SYS_RES_IRQ:
+ rm = &sc->sc_irq_rman;
+ break;
+ case SYS_RES_IOPORT:
+ rm = &sc->sc_io_rman;
+ break;
+ case SYS_RES_MEMORY:
+ rm = &sc->sc_mem_rman;
+ break;
+ default:
+ return (NULL);
+ }
+
+ rv = rman_reserve_resource(rm, start, end, count, flags, child);
+
+ if (rv == NULL)
+ return (NULL);
+
+ rman_set_rid(rv, *rid);
+
+ if ((flags & RF_ACTIVE) && type != SYS_RES_IRQ) {
+ if (bus_activate_resource(child, type, *rid, rv)) {
+ rman_release_resource(rv);
+ return (NULL);
+ }
+ }
+
+ return (rv);
+}
+
+static inline int
+mtk_idx_to_irq(int idx)
+{
+
+ return ((idx == 0) ? MTK_PCIE0_IRQ :
+ (idx == 1) ? MTK_PCIE1_IRQ :
+ (idx == 2) ? MTK_PCIE2_IRQ : -1);
+}
+
+static inline int
+mtk_irq_to_idx(int irq)
+{
+
+ return ((irq == MTK_PCIE0_IRQ) ? 0 :
+ (irq == MTK_PCIE1_IRQ) ? 1 :
+ (irq == MTK_PCIE2_IRQ) ? 2 : -1);
+}
+
+static void
+mtk_pci_mask_irq(void *source)
+{
+ MT_WRITE32(mt_sc, MTK_PCI_PCIENA,
+ MT_READ32(mt_sc, MTK_PCI_PCIENA) & ~(1<<((int)source)));
+}
+
+static void
+mtk_pci_unmask_irq(void *source)
+{
+
+ MT_WRITE32(mt_sc, MTK_PCI_PCIENA,
+ MT_READ32(mt_sc, MTK_PCI_PCIENA) | (1<<((int)source)));
+}
+
+static int
+mtk_pci_setup_intr(device_t bus, device_t child, struct resource *ires,
+ int flags, driver_filter_t *filt, driver_intr_t *handler,
+ void *arg, void **cookiep)
+{
+ struct mtk_pci_softc *sc = device_get_softc(bus);
+ struct intr_event *event;
+ int irq, error, irqidx;
+
+ irq = rman_get_start(ires);
+
+ if (irq < sc->sc_irq_start || irq > sc->sc_irq_end)
+ return (EINVAL);
+
+ irqidx = irq - sc->sc_irq_start;
+
+ event = sc->sc_eventstab[irqidx];
+ if (event == NULL) {
+ error = intr_event_create(&event, (void *)irq, 0, irq,
+ mtk_pci_mask_irq, mtk_pci_unmask_irq, NULL, NULL,
+ "pci intr%d:", irq);
+
+ if (error == 0) {
+ sc->sc_eventstab[irqidx] = event;
+ }
+ else {
+ return (error);
+ }
+ }
+
+ intr_event_add_handler(event, device_get_nameunit(child), filt,
+ handler, arg, intr_priority(flags), flags, cookiep);
+
+ mtk_pci_unmask_irq((void*)irq);
+
+ return (0);
+}
+
+static int
+mtk_pci_teardown_intr(device_t dev, device_t child, struct resource *ires,
+ void *cookie)
+{
+ struct mtk_pci_softc *sc = device_get_softc(dev);
+ int irq, result, irqidx;
+
+ irq = rman_get_start(ires);
+ if (irq < sc->sc_irq_start || irq > sc->sc_irq_end)
+ return (EINVAL);
+
+ irqidx = irq - sc->sc_irq_start;
+ if (sc->sc_eventstab[irqidx] == NULL)
+ panic("Trying to teardown unoccupied IRQ");
+
+ mtk_pci_mask_irq((void*)irq);
+
+ result = intr_event_remove_handler(cookie);
+ if (!result)
+ sc->sc_eventstab[irqidx] = NULL;
+
+
+ return (result);
+}
+
+static inline uint32_t
+mtk_pci_make_addr(int bus, int slot, int func, int reg)
+{
+ uint32_t addr;
+
+ addr = ((((reg & 0xf00) >> 8) << 24) | (bus << 16) | (slot << 11) |
+ (func << 8) | (reg & 0xfc) | (1 << 31));
+
+ return (addr);
+}
+
+static int
+mtk_pci_maxslots(device_t dev)
+{
+
+ return (PCI_SLOTMAX);
+}
+
+static inline int
+mtk_pci_slot_has_link(device_t dev, int slot)
+{
+ struct mtk_pci_softc *sc = device_get_softc(dev);
+
+ return !!(sc->pcie_link_status & (1<<slot));
+}
+
+static uint32_t
+mtk_pci_read_config(device_t dev, u_int bus, u_int slot, u_int func,
+ u_int reg, int bytes)
+{
+ struct mtk_pci_softc *sc = device_get_softc(dev);
+ uint32_t addr = 0, data = 0;
+
+ /* Return ~0U if slot has no link */
+ if (bus == 0 && mtk_pci_slot_has_link(dev, slot) == 0) {
+ return (~0U);
+ }
+
+ mtx_lock_spin(&mtk_pci_mtx);
+ addr = mtk_pci_make_addr(bus, slot, func, (reg & ~3)) & sc->addr_mask;
+ MT_WRITE32(sc, MTK_PCI_CFGADDR, addr);
+ switch (bytes % 4) {
+ case 0:
+ data = MT_READ32(sc, MTK_PCI_CFGDATA);
+ break;
+ case 1:
+ data = MT_READ8(sc, MTK_PCI_CFGDATA + (reg & 0x3));
+ break;
+ case 2:
+ data = MT_READ16(sc, MTK_PCI_CFGDATA + (reg & 0x3));
+ break;
+ default:
+ panic("%s(): Wrong number of bytes (%d) requested!\n",
+ __FUNCTION__, bytes % 4);
+ }
+ mtx_unlock_spin(&mtk_pci_mtx);
+
+ return (data);
+}
+
+static void
+mtk_pci_write_config(device_t dev, u_int bus, u_int slot, u_int func,
+ u_int reg, uint32_t val, int bytes)
+{
+ struct mtk_pci_softc *sc = device_get_softc(dev);
+ uint32_t addr = 0, data = val;
+
+ /* Do not write if slot has no link */
+ if (bus == 0 && mtk_pci_slot_has_link(dev, slot) == 0)
+ return;
+
+ mtx_lock_spin(&mtk_pci_mtx);
+ addr = mtk_pci_make_addr(bus, slot, func, (reg & ~3)) & sc->addr_mask;
+ MT_WRITE32(sc, MTK_PCI_CFGADDR, addr);
+ switch (bytes % 4) {
+ case 0:
+ MT_WRITE32(sc, MTK_PCI_CFGDATA, data);
+ break;
+ case 1:
+ MT_WRITE8(sc, MTK_PCI_CFGDATA + (reg & 0x3), data);
+ break;
+ case 2:
+ MT_WRITE16(sc, MTK_PCI_CFGDATA + (reg & 0x3), data);
+ break;
+ default:
+ panic("%s(): Wrong number of bytes (%d) requested!\n",
+ __FUNCTION__, bytes % 4);
+ }
+ mtx_unlock_spin(&mtk_pci_mtx);
+}
+
+#if 0
+/* We take care of interrupt routing in the allocator code below */
+static int
+mtk_pci_route_interrupt(device_t pcib, device_t device, int pin)
+{
+ //struct mtk_pci_softc *sc = device_get_softc(pcib);
+ int bus, sl, dev;
+
+ if (1) return PCI_INVALID_IRQ;
+
+ bus = pci_get_bus(device);
+ sl = pci_get_slot(device);
+ dev = pci_get_device(device);
+
+ printf("%s: for %d:%d:%d, int = %d\n", __FUNCTION__, bus, sl, dev, pin);
+
+ if (bus != 0)
+ panic("Unexpected bus number %d\n", bus);
+
+ /* PCIe only */
+ switch (sl) {
+ case 0: return MTK_PCIE0_IRQ;
+ case 1: return MTK_PCIE0_IRQ + 1;
+ case 2: return MTK_PCIE0_IRQ + 2;
+ default: return (-1);
+ }
+
+ return (-1);
+}
+#endif
+
+static device_method_t mtk_pci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, mtk_pci_probe),
+ DEVMETHOD(device_attach, mtk_pci_attach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ /* Bus interface */
+ DEVMETHOD(bus_read_ivar, mtk_pci_read_ivar),
+ DEVMETHOD(bus_write_ivar, mtk_pci_write_ivar),
+ DEVMETHOD(bus_alloc_resource, mtk_pci_alloc_resource),
+ DEVMETHOD(bus_release_resource, bus_generic_release_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_setup_intr, mtk_pci_setup_intr),
+ DEVMETHOD(bus_teardown_intr, mtk_pci_teardown_intr),
+
+ /* pcib interface */
+ DEVMETHOD(pcib_maxslots, mtk_pci_maxslots),
+ DEVMETHOD(pcib_read_config, mtk_pci_read_config),
+ DEVMETHOD(pcib_write_config, mtk_pci_write_config),
+#if 0
+ DEVMETHOD(pcib_route_interrupt, mtk_pci_route_interrupt),
+#endif
+
+ DEVMETHOD_END
+};
+
+static driver_t mtk_pci_driver = {
+ "pcib",
+ mtk_pci_methods,
+ sizeof(struct mtk_pci_softc),
+};
+
+static devclass_t mtk_pci_devclass;
+
+DRIVER_MODULE(mtk_pci, simplebus, mtk_pci_driver, mtk_pci_devclass, 0, 0);
+
+/* Resource allocation code */
+static inline uint32_t
+pcib_bit_get(uint32_t *map, uint32_t bit)
+{
+ uint32_t n = bit / BITS_PER_UINT32;
+
+ bit = bit % BITS_PER_UINT32;
+ return (map[n] & (1 << bit));
+}
+
+static inline void
+pcib_bit_set(uint32_t *map, uint32_t bit)
+{
+ uint32_t n = bit / BITS_PER_UINT32;
+
+ bit = bit % BITS_PER_UINT32;
+ map[n] |= (1 << bit);
+}
+
+static inline uint32_t
+pcib_map_check(uint32_t *map, uint32_t start, uint32_t bits)
+{
+ uint32_t i;
+
+ for (i = start; i < start + bits; i++)
+ if (pcib_bit_get(map, i))
+ return (0);
+
+ return (1);
+}
+
+static inline void
+pcib_map_set(uint32_t *map, uint32_t start, uint32_t bits)
+{
+ uint32_t i;
+
+ for (i = start; i < start + bits; i++)
+ pcib_bit_set(map, i);
+}
+
+static bus_addr_t
+pcib_alloc(device_t dev, uint32_t smask)
+{
+ struct mtk_pci_softc *sc = device_get_softc(dev);
+ uint32_t bits, bits_limit, i, *map, min_alloc, size;
+ bus_addr_t addr = 0;
+ bus_addr_t base;
+
+ if (smask & 1) {
+ base = sc->sc_io_base;
+ min_alloc = PCI_MIN_IO_ALLOC;
+ bits_limit = sc->sc_io_size / min_alloc;
+ map = sc->sc_io_map;
+ smask &= ~0x3;
+ } else {
+ base = sc->sc_mem_base;
+ min_alloc = PCI_MIN_MEM_ALLOC;
+ bits_limit = sc->sc_mem_size / min_alloc;
+ map = sc->sc_mem_map;
+ smask &= ~0xF;
+ }
+
+ size = ~smask + 1;
+ bits = size / min_alloc;
+
+ for (i = 0; i + bits <= bits_limit; i+= bits)
+ if (pcib_map_check(map, i, bits)) {
+ pcib_map_set(map, i, bits);
+ addr = base + (i * min_alloc);
+ return (addr);
+ }
+
+ return (addr);
+}
+
+static int
+mtk_pcib_init_bar(device_t dev, int bus, int slot, int func, int barno)
+{
+ uint32_t addr, bar;
+ int reg, width;
+
+ reg = PCIR_BAR(barno);
+
+ mtk_pci_write_config(dev, bus, slot, func, reg, ~0, 4);
+ bar = mtk_pci_read_config(dev, bus, slot, func, reg, 4);
+ if (bar == 0)
+ return (1);
+
+ /* Calculate BAR size: 64 or 32 bit (in 32-bit units) */
+ width = ((bar & 7) == 4) ? 2 : 1;
+
+ addr = pcib_alloc(dev, bar);
+ if (!addr)
+ return (-1);
+
+ if (bootverbose)
+ printf("PCI %u:%u:%u: reg %x: smask=%08x: addr=%08x\n",
+ bus, slot, func, reg, bar, addr);
+
+ mtk_pci_write_config(dev, bus, slot, func, reg, addr, 4);
+ if (width == 2)
+ mtk_pci_write_config(dev, bus, slot, func, reg + 4, 0, 4);
+
+ return (width);
+}
+
+static int
+mtk_pcib_init_all_bars(device_t dev, int bus, int slot, int func,
+ int hdrtype)
+{
+ int maxbar, bar, i;
+
+ maxbar = (hdrtype & PCIM_HDRTYPE) ? 0 : 6;
+ bar = 0;
+
+ while (bar < maxbar) {
+ i = mtk_pcib_init_bar(dev, bus, slot, func, bar);
+ bar += i;
+ if (i < 0) {
+ device_printf(dev, "PCI IO/Memory space exhausted\n");
+ return (ENOMEM);
+ }
+ }
+
+ return (0);
+}
+
+static void
+mtk_pcib_init_bridge(device_t dev, int bus, int slot, int func)
+{
+ struct mtk_pci_softc *sc = device_get_softc(dev);
+ bus_addr_t io_base, mem_base;
+ uint32_t io_limit, mem_limit;
+ int secbus;
+
+ if (bus == 0 && !mtk_pci_slot_has_link(dev, slot)) {
+ sc->sc_cur_secbus++;
+ device_printf(dev, "Skip bus %d due to no link\n",
+ sc->sc_cur_secbus);
+ return;
+ }
+
+ io_base = sc->sc_io_base;
+ io_limit = io_base + sc->sc_io_size - 1;
+ mem_base = sc->sc_mem_base;
+ mem_limit = mem_base + sc->sc_mem_size - 1;
+
+ mtk_pci_write_config(dev, bus, slot, func, PCIR_IOBASEL_1,
+ io_base >> 8, 1);
+ mtk_pci_write_config(dev, bus, slot, func, PCIR_IOBASEH_1,
+ io_base >> 16, 2);
+ mtk_pci_write_config(dev, bus, slot, func, PCIR_IOLIMITL_1,
+ io_limit >> 8, 1);
+ mtk_pci_write_config(dev, bus, slot, func, PCIR_IOLIMITH_1,
+ io_limit >> 16, 2);
+
+ mtk_pci_write_config(dev, bus, slot, func, PCIR_MEMBASE_1,
+ mem_base >> 16, 2);
+ mtk_pci_write_config(dev, bus, slot, func, PCIR_MEMLIMIT_1,
+ mem_limit >> 16, 2);
+
+ mtk_pci_write_config(dev, bus, slot, func, PCIR_PMBASEL_1,
+ 0x10, 2);
+ mtk_pci_write_config(dev, bus, slot, func, PCIR_PMBASEH_1,
+ 0x0, 4);
+ mtk_pci_write_config(dev, bus, slot, func, PCIR_PMLIMITL_1,
+ 0xF, 2);
+ mtk_pci_write_config(dev, bus, slot, func, PCIR_PMLIMITH_1,
+ 0x0, 4);
+
+ mtk_pci_write_config(dev, bus, slot, func, PCIR_INTLINE, 0xff, 1);
+
+ secbus = mtk_pci_read_config(dev, bus, slot, func, PCIR_SECBUS_1, 1);
+
+ if (secbus == 0) {
+ sc->sc_cur_secbus++;
+ mtk_pci_write_config(dev, bus, slot, func, PCIR_SECBUS_1,
+ sc->sc_cur_secbus, 1);
+ mtk_pci_write_config(dev, bus, slot, func, PCIR_SUBBUS_1,
+ sc->sc_cur_secbus, 1);
+ secbus = sc->sc_cur_secbus;
+ }
+
+ mtk_pcib_init(dev, secbus, PCI_SLOTMAX);
+}
+
+static uint8_t
+mtk_pci_get_int(device_t dev, int bus, int slot)
+{
+
+ if (slot != 0)
+ return (PCI_INVALID_IRQ);
+
+ switch (bus) {
+ case 1:
+ return (MTK_PCIE0_IRQ);
+ case 2:
+ return (MTK_PCIE1_IRQ);
+ case 3:
+ return (MTK_PCIE2_IRQ);
+ default:
+ device_printf(dev, "Bus %d out of range\n", slot);
+ return (PCI_INVALID_IRQ);
+ }
+
+ /* Unreachable */
+ return (PCI_INVALID_IRQ);
+}
+
+static int
+mtk_pcib_init(device_t dev, int bus, int maxslot)
+{
+ int slot, func, maxfunc, error;
+ uint8_t hdrtype, command, class, subclass;
+
+ for (slot = 0; slot <= maxslot; slot++) {
+ maxfunc = 0;
+ for (func = 0; func <= maxfunc; func++) {
+ hdrtype = mtk_pci_read_config(dev, bus, slot, func,
+ PCIR_HDRTYPE, 1);
+
+ if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE)
+ continue;
+
+ if (func == 0 && (hdrtype & PCIM_MFDEV))
+ maxfunc = PCI_FUNCMAX;
+
+ command = mtk_pci_read_config(dev, bus, slot, func,
+ PCIR_COMMAND, 1);
+ command &= ~(PCIM_CMD_MEMEN | PCIM_CMD_PORTEN);
+ mtk_pci_write_config(dev, bus, slot, func,
+ PCIR_COMMAND, command, 1);
+
+ error = mtk_pcib_init_all_bars(dev, bus, slot, func,
+ hdrtype);
+
+ if (error)
+ return (error);
+
+ command |= PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN |
+ PCIM_CMD_PORTEN;
+ mtk_pci_write_config(dev, bus, slot, func,
+ PCIR_COMMAND, command, 1);
+
+ mtk_pci_write_config(dev, bus, slot, func,
+ PCIR_CACHELNSZ, 16, 1);
+
+ class = mtk_pci_read_config(dev, bus, slot, func,
+ PCIR_CLASS, 1);
+ subclass = mtk_pci_read_config(dev, bus, slot, func,
+ PCIR_SUBCLASS, 1);
+
+ if (class != PCIC_BRIDGE ||
+ subclass != PCIS_BRIDGE_PCI) {
+ uint8_t val;
+
+ val = mtk_pci_get_int(dev, bus, slot);
+
+ mtk_pci_write_config(dev, bus, slot, func,
+ PCIR_INTLINE, val, 1); /* XXX */
+ continue;
+ }
+
+ mtk_pcib_init_bridge(dev, bus, slot, func);
+ }
+ }
+
+ return (0);
+}
+
+/* Our interrupt handler */
+static int
+mtk_pci_intr(void *arg)
+{
+ struct mtk_pci_softc *sc = arg;
+ struct intr_event *event;
+ uint32_t reg, irq, irqidx;
+
+ reg = MT_READ32(sc, MTK_PCI_PCIINT);
+
+ for (irq = sc->sc_irq_start; irq <= sc->sc_irq_end; irq++) {
+ if (reg & (1u<<irq)) {
+ irqidx = irq - sc->sc_irq_start;
+ event = sc->sc_eventstab[irqidx];
+ if (!event || TAILQ_EMPTY(&event->ie_handlers)) {
+ if (irq != 0)
+ printf("Stray PCI IRQ %d\n", irq);
+ continue;
+ }
+
+ intr_event_handle(event, NULL);
+ }
+ }
+
+ return (FILTER_HANDLED);
+}
+
+/* PCIe SoC-specific initialization */
+static int
+mtk_pcie_phy_init(device_t dev)
+{
+ struct mtk_pci_softc *sc;
+
+ /* Get our softc */
+ sc = device_get_softc(dev);
+
+ /* We don't know how many slots we have yet */
+ sc->num_slots = 0;
+
+ /* Handle SoC specific PCIe init */
+ switch (sc->socid) {
+ case MTK_SOC_MT7628: /* Fallthrough */
+ case MTK_SOC_MT7688:
+ if (mtk_pcie_phy_mt7628_init(dev))
+ return (ENXIO);
+ break;
+ case MTK_SOC_MT7621:
+ if (mtk_pcie_phy_mt7621_init(dev))
+ return (ENXIO);
+ break;
+ case MTK_SOC_MT7620A:
+ if (mtk_pcie_phy_mt7620_init(dev))
+ return (ENXIO);
+ break;
+ case MTK_SOC_RT3662: /* Fallthrough */
+ case MTK_SOC_RT3883:
+ if (mtk_pcie_phy_rt3883_init(dev))
+ return (ENXIO);
+ break;
+ default:
+ device_printf(dev, "unsupported device %x\n", sc->socid);
+ return (ENXIO);
+ }
+
+ /*
+ * If we were successful so far go and set up the PCIe slots, so we
+ * may allocate mem/io/irq resources and enumerate busses later.
+ */
+ mtk_pcie_phy_setup_slots(dev);
+
+ return (0);
+}
+
+static int
+mtk_pcie_phy_start(device_t dev)
+{
+ struct mtk_pci_softc *sc = device_get_softc(dev);
+
+ if (sc->socid == MTK_SOC_MT7621 &&
+ (mtk_sysctl_get(SYSCTL_REVID) & SYSCTL_REVID_MASK) !=
+ SYSCTL_MT7621_REV_E) {
+ if (fdt_reset_assert_all(dev))
+ return (ENXIO);
+ } else {
+ if (fdt_reset_deassert_all(dev))
+ return (ENXIO);
+ }
+
+ if (fdt_clock_enable_all(dev))
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+mtk_pcie_phy_stop(device_t dev)
+{
+ struct mtk_pci_softc *sc = device_get_softc(dev);
+
+ if (sc->socid == MTK_SOC_MT7621 &&
+ (mtk_sysctl_get(SYSCTL_REVID) & SYSCTL_REVID_MASK) !=
+ SYSCTL_MT7621_REV_E) {
+ if (fdt_reset_deassert_all(dev))
+ return (ENXIO);
+ } else {
+ if (fdt_reset_assert_all(dev))
+ return (ENXIO);
+ }
+
+ if (fdt_clock_disable_all(dev))
+ return (ENXIO);
+
+ return (0);
+}
+
+#define mtk_pcie_phy_set(_sc, _reg, _s, _n, _v) \
+ MT_WRITE32((_sc), (_reg), ((MT_READ32((_sc), (_reg)) & \
+ (~(((1ull << (_n)) - 1) << (_s)))) | ((_v) << (_s))))
+
+static void
+mtk_pcie_phy_mt7621_bypass_pipe_rst(struct mtk_pci_softc *sc, uint32_t off)
+{
+
+ mtk_pcie_phy_set(sc, off + 0x002c, 12, 1, 1);
+ mtk_pcie_phy_set(sc, off + 0x002c, 4, 1, 1);
+ mtk_pcie_phy_set(sc, off + 0x012c, 12, 1, 1);
+ mtk_pcie_phy_set(sc, off + 0x012c, 4, 1, 1);
+ mtk_pcie_phy_set(sc, off + 0x102c, 12, 1, 1);
+ mtk_pcie_phy_set(sc, off + 0x102c, 4, 1, 1);
+}
+
+static void
+mtk_pcie_phy_mt7621_setup_ssc(struct mtk_pci_softc *sc, uint32_t off)
+{
+ uint32_t xtal_sel;
+
+ xtal_sel = mtk_sysctl_get(SYSCTL_SYSCFG) >> 6;
+ xtal_sel &= 0x7;
+
+ mtk_pcie_phy_set(sc, off + 0x400, 8, 1, 1);
+ mtk_pcie_phy_set(sc, off + 0x400, 9, 2, 0);
+ mtk_pcie_phy_set(sc, off + 0x000, 4, 1, 1);
+ mtk_pcie_phy_set(sc, off + 0x100, 4, 1, 1);
+ mtk_pcie_phy_set(sc, off + 0x000, 5, 1, 0);
+ mtk_pcie_phy_set(sc, off + 0x100, 5, 1, 0);
+
+ if (xtal_sel <= 5 && xtal_sel >= 3) {
+ mtk_pcie_phy_set(sc, off + 0x490, 6, 2, 1);
+ mtk_pcie_phy_set(sc, off + 0x4a8, 0, 12, 0x1a);
+ mtk_pcie_phy_set(sc, off + 0x4a8, 16, 12, 0x1a);
+ } else {
+ mtk_pcie_phy_set(sc, off + 0x490, 6, 2, 0);
+ if (xtal_sel >= 6) {
+ mtk_pcie_phy_set(sc, off + 0x4bc, 4, 2, 0x01);
+ mtk_pcie_phy_set(sc, off + 0x49c, 0, 31, 0x18000000);
+ mtk_pcie_phy_set(sc, off + 0x4a4, 0, 16, 0x18d);
+ mtk_pcie_phy_set(sc, off + 0x4a8, 0, 12, 0x4a);
+ mtk_pcie_phy_set(sc, off + 0x4a8, 16, 12, 0x4a);
+ mtk_pcie_phy_set(sc, off + 0x4a8, 0, 12, 0x11);
+ mtk_pcie_phy_set(sc, off + 0x4a8, 16, 12, 0x11);
+ } else {
+ mtk_pcie_phy_set(sc, off + 0x4a8, 0, 12, 0x1a);
+ mtk_pcie_phy_set(sc, off + 0x4a8, 16, 12, 0x1a);
+ }
+ }
+
+ mtk_pcie_phy_set(sc, off + 0x4a0, 5, 1, 1);
+ mtk_pcie_phy_set(sc, off + 0x490, 22, 2, 2);
+ mtk_pcie_phy_set(sc, off + 0x490, 18, 4, 6);
+ mtk_pcie_phy_set(sc, off + 0x490, 12, 4, 2);
+ mtk_pcie_phy_set(sc, off + 0x490, 8, 4, 1);
+ mtk_pcie_phy_set(sc, off + 0x4ac, 16, 3, 0);
+ mtk_pcie_phy_set(sc, off + 0x490, 1, 3, 2);
+
+ if (xtal_sel <= 5 && xtal_sel >= 3) {
+ mtk_pcie_phy_set(sc, off + 0x414, 6, 2, 1);
+ mtk_pcie_phy_set(sc, off + 0x414, 5, 1, 1);
+ }
+
+ mtk_pcie_phy_set(sc, off + 0x414, 28, 2, 1);
+ mtk_pcie_phy_set(sc, off + 0x040, 17, 4, 7);
+ mtk_pcie_phy_set(sc, off + 0x040, 16, 1, 1);
+ mtk_pcie_phy_set(sc, off + 0x140, 17, 4, 7);
+ mtk_pcie_phy_set(sc, off + 0x140, 16, 1, 1);
+
+ mtk_pcie_phy_set(sc, off + 0x000, 5, 1, 1);
+ mtk_pcie_phy_set(sc, off + 0x100, 5, 1, 1);
+ mtk_pcie_phy_set(sc, off + 0x000, 4, 1, 0);
+ mtk_pcie_phy_set(sc, off + 0x100, 4, 1, 0);
+}
+
+/* XXX: ugly, we need to fix this at some point */
+#define MT7621_GPIO_CTRL0 *((volatile uint32_t *)0xbe000600)
+#define MT7621_GPIO_DATA0 *((volatile uint32_t *)0xbe000620)
+
+#define mtk_gpio_clr_set(_reg, _clr, _set) \
+ do { \
+ (_reg) = ((_reg) & (_clr)) | (_set); \
+ } while (0)
+
+static int
+mtk_pcie_phy_mt7621_init(device_t dev)
+{
+ struct mtk_pci_softc *sc = device_get_softc(dev);
+
+ /* First off, stop the PHY */
+ if (mtk_pcie_phy_stop(dev))
+ return (ENXIO);
+
+ /* PCIe resets are GPIO pins */
+ mtk_sysctl_clr_set(SYSCTL_GPIOMODE, MT7621_PERST_GPIO_MODE |
+ MT7621_UARTL3_GPIO_MODE, MT7621_PERST_GPIO | MT7621_UARTL3_GPIO);
+
+ /* Set GPIO pins as outputs */
+ mtk_gpio_clr_set(MT7621_GPIO_CTRL0, 0, MT7621_PCIE_RST);
+
+ /* Assert resets to PCIe devices */
+ mtk_gpio_clr_set(MT7621_GPIO_DATA0, MT7621_PCIE_RST, 0);
+
+ /* Give everything a chance to sink in */
+ DELAY(100000);
+
+ /* Now start the PHY again */
+ if (mtk_pcie_phy_start(dev))
+ return (ENXIO);
+
+ /* Wait for things to settle */
+ DELAY(100000);
+
+ /* Only apply below to REV-E hardware */
+ if ((mtk_sysctl_get(SYSCTL_REVID) & SYSCTL_REVID_MASK) ==
+ SYSCTL_MT7621_REV_E)
+ mtk_pcie_phy_mt7621_bypass_pipe_rst(sc, 0x9000);
+
+ /* Setup PCIe ports 0 and 1 */
+ mtk_pcie_phy_mt7621_setup_ssc(sc, 0x9000);
+ /* Setup PCIe port 2 */
+ mtk_pcie_phy_mt7621_setup_ssc(sc, 0xa000);
+
+ /* Deassert resets to PCIe devices */
+ mtk_gpio_clr_set(MT7621_GPIO_DATA0, 0, MT7621_PCIE_RST);
+
+ /* Set number of slots supported */
+ sc->num_slots = 3;
+
+ /* Give it a chance to sink in */
+ DELAY(100000);
+
+ return (0);
+}
+
+static void
+mtk_pcie_phy_mt7628_setup(struct mtk_pci_softc *sc, uint32_t off)
+{
+ uint32_t xtal_sel;
+
+ xtal_sel = mtk_sysctl_get(SYSCTL_SYSCFG) >> 6;
+ xtal_sel &= 0x1;
+
+ mtk_pcie_phy_set(sc, off + 0x400, 8, 1, 1);
+ mtk_pcie_phy_set(sc, off + 0x400, 9, 2, 0);
+ mtk_pcie_phy_set(sc, off + 0x000, 4, 1, 1);
+ mtk_pcie_phy_set(sc, off + 0x000, 5, 1, 0);
+ mtk_pcie_phy_set(sc, off + 0x4ac, 16, 3, 3);
+
+ if (xtal_sel == 1) {
+ mtk_pcie_phy_set(sc, off + 0x4bc, 24, 8, 0x7d);
+ mtk_pcie_phy_set(sc, off + 0x490, 12, 4, 0x08);
+ mtk_pcie_phy_set(sc, off + 0x490, 6, 2, 0x01);
+ mtk_pcie_phy_set(sc, off + 0x4c0, 0, 32, 0x1f400000);
+ mtk_pcie_phy_set(sc, off + 0x4a4, 0, 16, 0x013d);
+ mtk_pcie_phy_set(sc, off + 0x4a8, 16, 16, 0x74);
+ mtk_pcie_phy_set(sc, off + 0x4a8, 0, 16, 0x74);
+ } else {
+ mtk_pcie_phy_set(sc, off + 0x4bc, 24, 8, 0x64);
+ mtk_pcie_phy_set(sc, off + 0x490, 12, 4, 0x0a);
+ mtk_pcie_phy_set(sc, off + 0x490, 6, 2, 0x00);
+ mtk_pcie_phy_set(sc, off + 0x4c0, 0, 32, 0x19000000);
+ mtk_pcie_phy_set(sc, off + 0x4a4, 0, 16, 0x018d);
+ mtk_pcie_phy_set(sc, off + 0x4a8, 16, 16, 0x4a);
+ mtk_pcie_phy_set(sc, off + 0x4a8, 0, 16, 0x4a);
+ }
+
+ mtk_pcie_phy_set(sc, off + 0x498, 0, 8, 5);
+ mtk_pcie_phy_set(sc, off + 0x000, 5, 1, 1);
+ mtk_pcie_phy_set(sc, off + 0x000, 4, 1, 0);
+}
+
+static int
+mtk_pcie_phy_mt7628_init(device_t dev)
+{
+ struct mtk_pci_softc *sc = device_get_softc(dev);
+
+ /* Set PCIe reset to normal mode */
+ mtk_sysctl_clr_set(SYSCTL_GPIOMODE, MT7628_PERST_GPIO_MODE,
+ MT7628_PERST);
+
+ /* Start the PHY */
+ if (mtk_pcie_phy_start(dev))
+ return (ENXIO);
+
+ /* Give it a chance to sink in */
+ DELAY(100000);
+
+ /* Setup the PHY */
+ mtk_pcie_phy_mt7628_setup(sc, 0x9000);
+
+ /* Deassert PCIe device reset */
+ MT_CLR_SET32(sc, MTK_PCI_PCICFG, MTK_PCI_RESET, 0);
+
+ /* Set number of slots supported */
+ sc->num_slots = 1;
+
+ return (0);
+}
+
+static int
+mtk_pcie_phy_mt7620_wait_busy(struct mtk_pci_softc *sc)
+{
+ uint32_t reg_value, retry;
+
+ reg_value = retry = 0;
+
+ while (retry++ < MT7620_MAX_RETRIES) {
+ reg_value = MT_READ32(sc, MT7620_PCIE_PHY_CFG);
+ if (reg_value & PHY_BUSY)
+ DELAY(100000);
+ else
+ break;
+ }
+
+ if (retry >= MT7620_MAX_RETRIES)
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+mtk_pcie_phy_mt7620_set(struct mtk_pci_softc *sc, uint32_t reg,
+ uint32_t val)
+{
+ uint32_t reg_val;
+
+ if (mtk_pcie_phy_mt7620_wait_busy(sc))
+ return (ENXIO);
+
+ reg_val = PHY_MODE_WRITE | ((reg & 0xff) << PHY_ADDR_OFFSET) |
+ (val & 0xff);
+ MT_WRITE32(sc, MT7620_PCIE_PHY_CFG, reg_val);
+ DELAY(1000);
+
+ if (mtk_pcie_phy_mt7620_wait_busy(sc))
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+mtk_pcie_phy_mt7620_init(device_t dev)
+{
+ struct mtk_pci_softc *sc = device_get_softc(dev);
+
+ /*
+ * The below sets the PCIe PHY to bypass the PCIe DLL and enables
+ * "elastic buffer control", whatever that may be...
+ */
+ if (mtk_pcie_phy_mt7620_set(sc, 0x00, 0x80) ||
+ mtk_pcie_phy_mt7620_set(sc, 0x01, 0x04) ||
+ mtk_pcie_phy_mt7620_set(sc, 0x68, 0x84))
+ return (ENXIO);
+
+ /* Stop PCIe */
+ if (mtk_pcie_phy_stop(dev))
+ return (ENXIO);
+
+ /* Restore PPLL to a sane state before going on */
+ mtk_sysctl_clr_set(MT7620_PPLL_DRV, LC_CKDRVPD, PDRV_SW_SET);
+
+ /* No PCIe on the MT7620N */
+ if (!(mtk_sysctl_get(SYSCTL_REVID) & MT7620_PKG_BGA)) {
+ device_printf(dev, "PCIe disabled for MT7620N\n");
+ mtk_sysctl_clr_set(MT7620_PPLL_CFG0, 0, PPLL_SW_SET);
+ mtk_sysctl_clr_set(MT7620_PPLL_CFG1, 0, PPLL_PD);
+ return (ENXIO);
+ }
+
+ /* PCIe device reset pin is in normal mode */
+ mtk_sysctl_clr_set(SYSCTL_GPIOMODE, MT7620_PERST_GPIO_MODE,
+ MT7620_PERST);
+
+ /* Enable PCIe now */
+ if (mtk_pcie_phy_start(dev))
+ return (ENXIO);
+
+ /* Give it a chance to sink in */
+ DELAY(100000);
+
+ /* If PLL is not locked - bail */
+ if (!(mtk_sysctl_get(MT7620_PPLL_CFG1) & PPLL_LOCKED)) {
+ device_printf(dev, "no PPLL not lock\n");
+ mtk_pcie_phy_stop(dev);
+ return (ENXIO);
+ }
+
+ /* Configure PCIe PLL */
+ mtk_sysctl_clr_set(MT7620_PPLL_DRV, LC_CKDRVOHZ | LC_CKDRVHZ,
+ LC_CKDRVPD | PDRV_SW_SET);
+
+ /* and give it a chance to settle */
+ DELAY(100000);
+
+ /* Deassert PCIe device reset */
+ MT_CLR_SET32(sc, MTK_PCI_PCICFG, MTK_PCI_RESET, 0);
+
+ /* MT7620 supports one PCIe slot */
+ sc->num_slots = 1;
+
+ return (0);
+}
+
+static int
+mtk_pcie_phy_rt3883_init(device_t dev)
+{
+ struct mtk_pci_softc *sc = device_get_softc(dev);
+
+ /* Enable PCI host mode and PCIe RC mode */
+ mtk_sysctl_clr_set(SYSCTL_SYSCFG1, 0, RT3883_PCI_HOST_MODE |
+ RT3883_PCIE_RC_MODE);
+
+ /* Enable PCIe PHY */
+ if (mtk_pcie_phy_start(dev))
+ return (ENXIO);
+
+ /* Disable PCI, we only support PCIe for now */
+ mtk_sysctl_clr_set(SYSCTL_RSTCTRL, 0, RT3883_PCI_RST);
+ mtk_sysctl_clr_set(SYSCTL_CLKCFG1, RT3883_PCI_CLK, 0);
+
+ /* Give things a chance to sink in */
+ DELAY(500000);
+
+ /* Set PCIe port number to 0 and lift PCIe reset */
+ MT_WRITE32(sc, MTK_PCI_PCICFG, 0);
+
+ /* Configure PCI Arbiter */
+ MT_WRITE32(sc, MTK_PCI_ARBCTL, 0x79);
+
+ /* We have a single PCIe slot */
+ sc->num_slots = 1;
+
+ return (0);
+}
+
+static void
+mtk_pcie_phy_setup_slots(device_t dev)
+{
+ struct mtk_pci_softc *sc = device_get_softc(dev);
+ uint32_t bar0_val, val;
+ int i;
+
+ /* Disable all PCIe interrupts */
+ MT_WRITE32(sc, MTK_PCI_PCIENA, 0);
+
+ /* Default bar0_val is 64M, enabled */
+ bar0_val = 0x03FF0001;
+
+ /* But we override it to 2G, enabled for some SoCs */
+ if (sc->socid == MTK_SOC_MT7620A || sc->socid == MTK_SOC_MT7628 ||
+ sc->socid == MTK_SOC_MT7688 || sc->socid == MTK_SOC_MT7621)
+ bar0_val = 0x7FFF0001;
+
+ /* We still don't know which slots have linked up */
+ sc->pcie_link_status = 0;
+
+ /* XXX: I am not sure if this delay is really necessary */
+ DELAY(500000);
+
+ /*
+ * See which slots have links and mark them.
+ * Set up all slots' BARs and make them look like PCIe bridges.
+ */
+ for (i = 0; i < sc->num_slots; i++) {
+ /* If slot has link - mark it */
+ if (MT_READ32(sc, MTK_PCIE_STATUS(i)) & 1)
+ sc->pcie_link_status |= (1<<i);
+
+ /* Generic slot configuration follows */
+
+ /* We enable BAR0 */
+ MT_WRITE32(sc, MTK_PCIE_BAR0SETUP(i), bar0_val);
+ /* and disable BAR1 */
+ MT_WRITE32(sc, MTK_PCIE_BAR1SETUP(i), 0);
+ /* Internal memory base has no offset */
+ MT_WRITE32(sc, MTK_PCIE_IMBASEBAR0(i), 0);
+ /* We're a PCIe bridge */
+ MT_WRITE32(sc, MTK_PCIE_CLASS(i), 0x06040001);
+
+ val = mtk_pci_read_config(dev, 0, i, 0, 0x4, 4);
+ mtk_pci_write_config(dev, 0, i, 0, 0x4, val | 0x4, 4);
+ val = mtk_pci_read_config(dev, 0, i, 0, 0x70c, 4);
+ val &= ~(0xff << 8);
+ val |= (0x50 << 8);
+ mtk_pci_write_config(dev, 0, i, 0, 0x4, val, 4);
+ }
+}
diff --git a/sys/mips/mediatek/mtk_pcie.h b/sys/mips/mediatek/mtk_pcie.h
new file mode 100644
index 0000000..f4aa1ed
--- /dev/null
+++ b/sys/mips/mediatek/mtk_pcie.h
@@ -0,0 +1,164 @@
+/*-
+ * Copyright (c) 2016 Stanislav Galabov.
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+#ifndef __MTK_PCIE_H__
+#define __MTK_PCIE_H__
+
+#define PCI_MIN_IO_ALLOC 4
+#define PCI_MIN_MEM_ALLOC 16
+#define BITS_PER_UINT32 (NBBY * sizeof(uint32_t))
+
+#define MTK_PCI_NIRQS 3
+#define MTK_PCI_BASESLOT 0
+
+struct mtk_pci_softc {
+ device_t sc_dev;
+
+ struct resource * pci_res[MTK_PCI_NIRQS + 1];
+ void * pci_intrhand[MTK_PCI_NIRQS];
+
+ int sc_busno;
+ int sc_cur_secbus;
+
+ struct rman sc_mem_rman;
+ struct rman sc_io_rman;
+ struct rman sc_irq_rman;
+
+ uint32_t sc_num_irq;
+ uint32_t sc_irq_start;
+ uint32_t sc_irq_end;
+
+ bus_addr_t sc_mem_base;
+ bus_addr_t sc_mem_size;
+ uint32_t sc_mem_map[(256*1024*1024) /
+ (PCI_MIN_MEM_ALLOC * BITS_PER_UINT32)];
+
+ bus_addr_t sc_io_base;
+ bus_addr_t sc_io_size;
+ uint32_t sc_io_map[(16*1024*1024) /
+ (PCI_MIN_IO_ALLOC * BITS_PER_UINT32)];
+
+ struct intr_event *sc_eventstab[MTK_PCI_NIRQS];
+
+ uint32_t pcie_link_status;
+ uint32_t num_slots;
+ uint32_t socid;
+ uint32_t addr_mask;
+};
+
+#define MTK_PCI_PCICFG 0x0000
+#define MTK_PCI_RESET (1<<1)
+#define MTK_PCI_PCIINT 0x0008
+#define MTK_PCI_PCIENA 0x000C
+#define MTK_PCI_CFGADDR 0x0020
+#define MTK_PCI_CFGDATA 0x0024
+#define MTK_PCI_MEMBASE 0x0028
+#define MTK_PCI_IOBASE 0x002C
+#define MTK_PCI_ARBCTL 0x0080
+#define MTK_PCI_PHY0_CFG 0x0090
+
+#define MTK_PCI_PCIE0_BAR0SETUP 0x2010
+#define MTK_PCI_PCIE0_BAR1SETUP 0x2014
+#define MTK_PCI_PCIE0_IMBASEBAR0 0x2018
+#define MTK_PCI_PCIE0_ID 0x2030
+#define MTK_PCI_PCIE0_CLASS 0x2034
+#define MTK_PCI_PCIE0_SUBID 0x2038
+#define MTK_PCI_PCIE0_STATUS 0x2050
+#define MTK_PCI_PCIE0_DLECR 0x2060
+#define MTK_PCI_PCIE0_ECRC 0x2064
+
+#define MTK_PCIE_BAR0SETUP(_s) (MTK_PCI_PCIE0_BAR0SETUP + (_s)*0x1000)
+#define MTK_PCIE_BAR1SETUP(_s) (MTK_PCI_PCIE0_BAR1SETUP + (_s)*0x1000)
+#define MTK_PCIE_IMBASEBAR0(_s) (MTK_PCI_PCIE0_IMBASEBAR0 + (_s)*0x1000)
+#define MTK_PCIE_ID(_s) (MTK_PCI_PCIE0_ID + (_s)*0x1000)
+#define MTK_PCIE_CLASS(_s) (MTK_PCI_PCIE0_CLASS + (_s)*0x1000)
+#define MTK_PCIE_SUBID(_s) (MTK_PCI_PCIE0_SUBID + (_s)*0x1000)
+#define MTK_PCIE_STATUS(_s) (MTK_PCI_PCIE0_STATUS + (_s)*0x1000)
+
+#define MTK_PCIE0_IRQ 20
+#define MTK_PCIE1_IRQ 21
+#define MTK_PCIE2_IRQ 22
+
+#define MTK_PCI_INTR_PIN 2
+
+/* Chip specific defines */
+#define MT7620_MAX_RETRIES 10
+#define MT7620_PCIE_PHY_CFG 0x90
+#define PHY_BUSY (1<<31)
+#define PHY_MODE_WRITE (1<<23)
+#define PHY_ADDR_OFFSET 8
+#define MT7620_PPLL_CFG0 0x98
+#define PPLL_SW_SET (1<<31)
+#define MT7620_PPLL_CFG1 0x9c
+#define PPLL_PD (1<<26)
+#define PPLL_LOCKED (1<<23)
+#define MT7620_PPLL_DRV 0xa0
+#define PDRV_SW_SET (1<<31)
+#define LC_CKDRVPD (1<<19)
+#define LC_CKDRVOHZ (1<<18)
+#define LC_CKDRVHZ (1<<17)
+#define MT7620_PERST_GPIO_MODE (3<<16)
+#define MT7620_PERST (0<<16)
+#define MT7620_GPIO (2<<16)
+#define MT7620_PKG_BGA (1<<16)
+
+#define MT7628_PERST_GPIO_MODE (1<<16)
+#define MT7628_PERST (0<<16)
+
+#define MT7621_PERST_GPIO_MODE (3<<10)
+#define MT7621_PERST_GPIO (1<<10)
+#define MT7621_UARTL3_GPIO_MODE (3<<3)
+#define MT7621_UARTL3_GPIO (1<<3)
+#define MT7621_PCIE0_RST (1<<19)
+#define MT7621_PCIE1_RST (1<<8)
+#define MT7621_PCIE2_RST (1<<7)
+#define MT7621_PCIE_RST (MT7621_PCIE0_RST | MT7621_PCIE1_RST | \
+ MT7621_PCIE2_RST)
+
+#define RT3883_PCI_RST (1<<24)
+#define RT3883_PCI_CLK (1<<19)
+#define RT3883_PCI_HOST_MODE (1<<7)
+#define RT3883_PCIE_RC_MODE (1<<8)
+/* End of chip specific defines */
+
+#define MT_WRITE32(sc, off, val) \
+ bus_write_4((sc)->pci_res[0], (off), (val))
+#define MT_WRITE16(sc, off, val) \
+ bus_write_2((sc)->pci_res[0], (off), (val))
+#define MT_WRITE8(sc, off, val) \
+ bus_write_1((sc)->pci_res[0], (off), (val))
+#define MT_READ32(sc, off) \
+ bus_read_4((sc)->pci_res[0], (off))
+#define MT_READ16(sc, off) \
+ bus_read_2((sc)->pci_res[0], (off))
+#define MT_READ8(sc, off) \
+ bus_read_1((sc)->pci_res[0], (off))
+
+#define MT_CLR_SET32(sc, off, clr, set) \
+ MT_WRITE32((sc), (off), ((MT_READ32((sc), (off)) & ~(clr)) | (off)))
+
+#endif /* __MTK_PCIE_H__ */
diff --git a/sys/mips/mediatek/mtk_pinctrl.c b/sys/mips/mediatek/mtk_pinctrl.c
new file mode 100644
index 0000000..489be5b
--- /dev/null
+++ b/sys/mips/mediatek/mtk_pinctrl.c
@@ -0,0 +1,114 @@
+/*-
+ * Copyright (c) 2016 Stanislav Galabov
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.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 <dev/fdt/fdt_pinctrl.h>
+#include <mips/mediatek/mtk_sysctl.h>
+
+#include "fdt_pinctrl_if.h"
+
+static const struct ofw_compat_data compat_data[] = {
+ { "ralink,rt2880-pinctrl", 1 },
+
+ /* Sentinel */
+ { NULL, 0 }
+};
+
+static int
+mtk_pinctrl_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 Pin Controller");
+
+ return (0);
+}
+
+static int
+mtk_pinctrl_attach(device_t dev)
+{
+
+ if (device_get_unit(dev) != 0) {
+ device_printf(dev, "Only one pin control allowed\n");
+ return (ENXIO);
+ }
+
+ fdt_pinctrl_register(dev, "pinctrl-single,bits");
+ fdt_pinctrl_configure_tree(dev);
+
+ if (bootverbose)
+ device_printf(dev, "GPIO mode: 0x%08x\n",
+ mtk_sysctl_get(SYSCTL_GPIOMODE));
+
+ return (0);
+}
+
+static int
+mtk_pinctrl_configure(device_t dev, phandle_t cfgxref)
+{
+
+ return (EINVAL);
+}
+
+static device_method_t mtk_pinctrl_methods[] = {
+ DEVMETHOD(device_probe, mtk_pinctrl_probe),
+ DEVMETHOD(device_attach, mtk_pinctrl_attach),
+
+ /* fdt_pinctrl interface */
+ DEVMETHOD(fdt_pinctrl_configure, mtk_pinctrl_configure),
+
+ DEVMETHOD_END
+};
+
+static driver_t mtk_pinctrl_driver = {
+ "pinctrl",
+ mtk_pinctrl_methods,
+ 0,
+};
+static devclass_t mtk_pinctrl_devclass;
+
+EARLY_DRIVER_MODULE(mtk_pinctrl, simplebus, mtk_pinctrl_driver,
+ mtk_pinctrl_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_EARLY);
+
+MODULE_DEPEND(mtk_pinctrl, mtk_sysctl, 1, 1, 1);
diff --git a/sys/mips/mediatek/mtk_reset.c b/sys/mips/mediatek/mtk_reset.c
new file mode 100644
index 0000000..31d56e4
--- /dev/null
+++ b/sys/mips/mediatek/mtk_reset.c
@@ -0,0 +1,139 @@
+/*-
+ * Copyright (c) 2016 Stanislav Galabov
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.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 <mips/mediatek/fdt_reset.h>
+#include <mips/mediatek/mtk_sysctl.h>
+
+#include "fdt_reset_if.h"
+
+static const struct ofw_compat_data compat_data[] = {
+ { "ralink,rt2880-reset", 1 },
+
+ /* Sentinel */
+ { NULL, 0 }
+};
+
+static int
+mtk_reset_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 Reset Controller");
+
+ return (0);
+}
+
+static int
+mtk_reset_attach(device_t dev)
+{
+
+ if (device_get_unit(dev) != 0) {
+ device_printf(dev, "Only one reset control allowed\n");
+ return (ENXIO);
+ }
+
+ fdt_reset_register_provider(dev);
+
+ return (0);
+}
+
+#define RESET_ASSERT 1
+#define RESET_DEASSERT 0
+
+static int
+mtk_reset_set(device_t dev, int index, int value)
+{
+ uint32_t mask;
+
+ /* index 0 is SoC reset, indices 1 - 31 are valid peripheral resets */
+ if (index < 1 || index > 31)
+ return (EINVAL);
+
+ mask = (1u << index);
+
+ if (value == RESET_ASSERT)
+ mtk_sysctl_clr_set(SYSCTL_RSTCTRL, 0, mask);
+ else
+ mtk_sysctl_clr_set(SYSCTL_RSTCTRL, mask, 0);
+
+ return (0);
+}
+
+static int
+mtk_reset_assert(device_t dev, int index)
+{
+
+ return mtk_reset_set(dev, index, RESET_ASSERT);
+}
+
+static int
+mtk_reset_deassert(device_t dev, int index)
+{
+
+ return mtk_reset_set(dev, index, RESET_DEASSERT);
+}
+
+static device_method_t mtk_reset_methods[] = {
+ DEVMETHOD(device_probe, mtk_reset_probe),
+ DEVMETHOD(device_attach, mtk_reset_attach),
+
+ /* fdt_reset interface */
+ DEVMETHOD(fdt_reset_assert, mtk_reset_assert),
+ DEVMETHOD(fdt_reset_deassert, mtk_reset_deassert),
+
+ DEVMETHOD_END
+};
+
+static driver_t mtk_reset_driver = {
+ "rstctrl",
+ mtk_reset_methods,
+ 0,
+};
+static devclass_t mtk_reset_devclass;
+
+EARLY_DRIVER_MODULE(mtk_reset, simplebus, mtk_reset_driver, mtk_reset_devclass,
+ 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_EARLY);
+
+MODULE_DEPEND(mtk_reset, mtk_sysctl, 1, 1, 1);
diff --git a/sys/mips/mediatek/mtk_soc.c b/sys/mips/mediatek/mtk_soc.c
new file mode 100644
index 0000000..b331467
--- /dev/null
+++ b/sys/mips/mediatek/mtk_soc.c
@@ -0,0 +1,438 @@
+/*-
+ * Copyright (c) 2016 Stanislav Galabov.
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+
+#include <machine/fdt.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/fdt/fdt_clock.h>
+
+#include <mips/mediatek/fdt_reset.h>
+#include <mips/mediatek/mtk_sysctl.h>
+#include <mips/mediatek/mtk_soc.h>
+
+static uint32_t mtk_soc_socid = MTK_SOC_UNKNOWN;
+static uint32_t mtk_soc_uartclk = 0;
+static uint32_t mtk_soc_cpuclk = MTK_CPU_CLK_880MHZ;
+static uint32_t mtk_soc_timerclk = MTK_CPU_CLK_880MHZ / 2;
+
+static const struct ofw_compat_data compat_data[] = {
+ { "ralink,rt3050-soc", MTK_SOC_RT3050 },
+ { "ralink,rt3052-soc", MTK_SOC_RT3052 },
+ { "ralink,rt3350-soc", MTK_SOC_RT3350 },
+ { "ralink,rt3352-soc", MTK_SOC_RT3352 },
+ { "ralink,rt3662-soc", MTK_SOC_RT3662 },
+ { "ralink,rt3883-soc", MTK_SOC_RT3883 },
+ { "ralink,rt5350-soc", MTK_SOC_RT5350 },
+ { "ralink,mtk7620a-soc", MTK_SOC_MT7620A },
+ { "ralink,mtk7620n-soc", MTK_SOC_MT7620N },
+ { "mediatek,mtk7621-soc", MTK_SOC_MT7621 },
+ { "ralink,mtk7621-soc", MTK_SOC_MT7621 },
+ { "ralink,mtk7628an-soc", MTK_SOC_MT7628 },
+ { "mediatek,mt7628an-soc", MTK_SOC_MT7628 },
+ { "ralink,mtk7688-soc", MTK_SOC_MT7688 },
+
+ /* Sentinel */
+ { NULL, MTK_SOC_UNKNOWN },
+};
+
+static uint32_t
+mtk_detect_cpuclk_rt305x(bus_space_tag_t bst, bus_space_handle_t bsh)
+{
+ uint32_t clk;
+
+ clk = bus_space_read_4(bst, bsh, SYSCTL_SYSCFG);
+ clk >>= RT305X_CPU_CLKSEL_OFF;
+ clk &= RT305X_CPU_CLKSEL_MSK;
+
+ return ((clk == 0) ? MTK_CPU_CLK_320MHZ : MTK_CPU_CLK_384MHZ);
+}
+
+static uint32_t
+mtk_detect_cpuclk_rt3352(bus_space_tag_t bst, bus_space_handle_t bsh)
+{
+ uint32_t val;
+
+ val = bus_space_read_4(bst, bsh, SYSCTL_SYSCFG);
+ val >>= RT3352_CPU_CLKSEL_OFF;
+ val &= RT3352_CPU_CLKSEL_MSK;
+
+ if (val)
+ return (MTK_CPU_CLK_400MHZ);
+
+ return (MTK_CPU_CLK_384MHZ);
+}
+
+static uint32_t
+mtk_detect_cpuclk_rt3883(bus_space_tag_t bst, bus_space_handle_t bsh)
+{
+ uint32_t val;
+
+ val = bus_space_read_4(bst, bsh, SYSCTL_SYSCFG);
+ val >>= RT3883_CPU_CLKSEL_OFF;
+ val &= RT3883_CPU_CLKSEL_MSK;
+
+ switch (val) {
+ case 0:
+ return (MTK_CPU_CLK_250MHZ);
+ case 1:
+ return (MTK_CPU_CLK_384MHZ);
+ case 2:
+ return (MTK_CPU_CLK_480MHZ);
+ case 3:
+ return (MTK_CPU_CLK_500MHZ);
+ }
+
+ /* Never reached */
+ return (0);
+}
+
+static uint32_t
+mtk_detect_cpuclk_rt5350(bus_space_tag_t bst, bus_space_handle_t bsh)
+{
+ uint32_t val1, val2;
+
+ val1 = val2 = bus_space_read_4(bst, bsh, SYSCTL_SYSCFG);
+
+ val1 >>= RT5350_CPU_CLKSEL_OFF1;
+ val2 >>= RT5350_CPU_CLKSEL_OFF2;
+ val1 &= RT5350_CPU_CLKSEL_MSK;
+ val2 &= RT5350_CPU_CLKSEL_MSK;
+ val1 |= (val2 << 1);
+
+ switch (val1) {
+ case 0:
+ return (MTK_CPU_CLK_360MHZ);
+ case 1:
+ /* Reserved value, but we return UNKNOWN */
+ return (MTK_CPU_CLK_UNKNOWN);
+ case 2:
+ return (MTK_CPU_CLK_320MHZ);
+ case 3:
+ return (MTK_CPU_CLK_300MHZ);
+ }
+
+ /* Never reached */
+ return (0);
+}
+
+static uint32_t
+mtk_detect_cpuclk_mt7620(bus_space_tag_t bst, bus_space_handle_t bsh)
+{
+ uint32_t val, mul, div, res;
+
+ val = bus_space_read_4(bst, bsh, SYSCTL_MT7620_CPLL_CFG1);
+ if (val & MT7620_CPU_CLK_AUX0)
+ return (MTK_CPU_CLK_480MHZ);
+
+ val = bus_space_read_4(bst, bsh, SYSCTL_MT7620_CPLL_CFG0);
+ if (!(val & MT7620_CPLL_SW_CFG))
+ return (MTK_CPU_CLK_600MHZ);
+
+ mul = MT7620_PLL_MULT_RATIO_BASE + ((val >> MT7620_PLL_MULT_RATIO_OFF) &
+ MT7620_PLL_MULT_RATIO_MSK);
+ div = (val >> MT7620_PLL_DIV_RATIO_OFF) & MT7620_PLL_DIV_RATIO_MSK;
+
+ if (div != MT7620_PLL_DIV_RATIO_MSK)
+ div += MT7620_PLL_DIV_RATIO_BASE;
+ else
+ div = MT7620_PLL_DIV_RATIO_MAX;
+
+ res = (MT7620_XTAL_40 * mul) / div;
+
+ return (MTK_MHZ(res));
+}
+
+static uint32_t
+mtk_detect_cpuclk_mt7621(bus_space_tag_t bst, bus_space_handle_t bsh)
+{
+ uint32_t val, div, res;
+
+ val = bus_space_read_4(bst, bsh, SYSCTL_CLKCFG0);
+ if (val & MT7621_USES_MEMDIV) {
+ div = bus_space_read_4(bst, bsh, MTK_MT7621_CLKDIV_REG);
+ div >>= MT7621_MEMDIV_OFF;
+ div &= MT7621_MEMDIV_MSK;
+ div += MT7621_MEMDIV_BASE;
+
+ val = bus_space_read_4(bst, bsh, SYSCTL_SYSCFG);
+ val >>= MT7621_CLKSEL_OFF;
+ val &= MT7621_CLKSEL_MSK;
+
+ if (val >= MT7621_CLKSEL_25MHZ_VAL)
+ res = div * MT7621_CLKSEL_25MHZ;
+ else if (val >= MT7621_CLKSEL_20MHZ_VAL)
+ res = div * MT7621_CLKSEL_20MHZ;
+ else
+ res = div * 0; /* XXX: not sure about this */
+ } else {
+ val = bus_space_read_4(bst, bsh, SYSCTL_CUR_CLK_STS);
+ div = (val >> MT7621_CLK_STS_DIV_OFF) & MT7621_CLK_STS_MSK;
+ val &= MT7621_CLK_STS_MSK;
+
+ res = (MT7621_CLK_STS_BASE * val) / div;
+ }
+
+ return (MTK_MHZ(res));
+}
+
+static uint32_t
+mtk_detect_cpuclk_mt7628(bus_space_tag_t bst, bus_space_handle_t bsh)
+{
+ uint32_t val;
+
+ val = bus_space_read_4(bst, bsh, SYSCTL_SYSCFG);
+ val >>= MT7628_CPU_CLKSEL_OFF;
+ val &= MT7628_CPU_CLKSEL_MSK;
+
+ if (val)
+ return (MTK_CPU_CLK_580MHZ);
+
+ return (MTK_CPU_CLK_575MHZ);
+}
+
+void
+mtk_soc_try_early_detect(void)
+{
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ uint32_t base;
+ phandle_t node;
+ int i;
+
+ if ((node = OF_finddevice("/")) == -1)
+ return;
+
+ for (i = 0; compat_data[i].ocd_str != NULL; i++) {
+ if (fdt_is_compatible(node, compat_data[i].ocd_str)) {
+ mtk_soc_socid = compat_data[i].ocd_data;
+ break;
+ }
+ }
+
+ if (mtk_soc_socid == MTK_SOC_UNKNOWN) {
+ /* We don't know the SoC, so we don't know how to get clocks */
+ return;
+ }
+
+ bst = fdtbus_bs_tag;
+ if (mtk_soc_socid == MTK_SOC_MT7621)
+ base = MTK_MT7621_BASE;
+ else
+ base = MTK_DEFAULT_BASE;
+
+ if (bus_space_map(bst, base, MTK_DEFAULT_SIZE, 0, &bsh))
+ return;
+
+ /* First, figure out the CPU clock */
+ switch (mtk_soc_socid) {
+ case MTK_SOC_RT3050: /* fallthrough */
+ case MTK_SOC_RT3052:
+ mtk_soc_cpuclk = mtk_detect_cpuclk_rt305x(bst, bsh);
+ break;
+ case MTK_SOC_RT3350:
+ mtk_soc_cpuclk = MTK_CPU_CLK_320MHZ;
+ break;
+ case MTK_SOC_RT3352:
+ mtk_soc_cpuclk = mtk_detect_cpuclk_rt3352(bst, bsh);
+ break;
+ case MTK_SOC_RT3662: /* fallthrough */
+ case MTK_SOC_RT3883:
+ mtk_soc_cpuclk = mtk_detect_cpuclk_rt3883(bst, bsh);
+ break;
+ case MTK_SOC_RT5350:
+ mtk_soc_cpuclk = mtk_detect_cpuclk_rt5350(bst, bsh);
+ break;
+ case MTK_SOC_MT7620A: /* fallthrough */
+ case MTK_SOC_MT7620N:
+ mtk_soc_cpuclk = mtk_detect_cpuclk_mt7620(bst, bsh);
+ break;
+ case MTK_SOC_MT7621:
+ mtk_soc_cpuclk = mtk_detect_cpuclk_mt7621(bst, bsh);
+ break;
+ case MTK_SOC_MT7628: /* fallthrough */
+ case MTK_SOC_MT7688:
+ mtk_soc_cpuclk = mtk_detect_cpuclk_mt7628(bst, bsh);
+ break;
+ default:
+ /* We don't know the SoC, so we can't find the CPU clock */
+ break;
+ }
+
+ /* Now figure out the timer clock */
+ if (mtk_soc_socid == MTK_SOC_MT7621) {
+#ifdef notyet
+ /*
+ * We use the GIC timer for timing source and its clock freq is
+ * the same as the CPU's clock freq
+ */
+ mtk_soc_timerclk = mtk_soc_cpuclk;
+#else
+ /*
+ * When GIC timer and MIPS timer are ready to co-exist and
+ * GIC timer is actually implemented, we need to switch to it.
+ * Until then we use a fake GIC timer, which is actually a
+ * normal MIPS ticker, so the timer clock is half the CPU clock
+ */
+ mtk_soc_timerclk = mtk_soc_cpuclk / 2;
+#endif
+ } else {
+ /*
+ * We use the MIPS ticker for the rest for now, so
+ * the CPU clock is divided by 2
+ */
+ mtk_soc_timerclk = mtk_soc_cpuclk / 2;
+ }
+
+ switch (mtk_soc_socid) {
+ case MTK_SOC_RT3350: /* fallthrough */
+ case MTK_SOC_RT3050: /* fallthrough */
+ case MTK_SOC_RT3052:
+ /* UART clock is CPU clock / 3 */
+ mtk_soc_uartclk = mtk_soc_cpuclk / MTK_UARTDIV_3;
+ break;
+ case MTK_SOC_RT3352: /* fallthrough */
+ case MTK_SOC_RT3662: /* fallthrough */
+ case MTK_SOC_RT3883: /* fallthrough */
+ case MTK_SOC_RT5350: /* fallthrough */
+ case MTK_SOC_MT7620A: /* fallthrough */
+ case MTK_SOC_MT7620N: /* fallthrough */
+ case MTK_SOC_MT7628: /* fallthrough */
+ case MTK_SOC_MT7688:
+ /* UART clock is always 40MHz */
+ mtk_soc_uartclk = MTK_UART_CLK_40MHZ;
+ break;
+ case MTK_SOC_MT7621:
+ /* UART clock is always 50MHz */
+ mtk_soc_uartclk = MTK_UART_CLK_50MHZ;
+ break;
+ default:
+ /* We don't know the SoC, so we don't know the UART clock */
+ break;
+ }
+
+ bus_space_unmap(bst, bsh, MTK_DEFAULT_SIZE);
+}
+
+uint32_t
+mtk_soc_get_uartclk(void)
+{
+
+ return mtk_soc_uartclk;
+}
+
+uint32_t
+mtk_soc_get_cpuclk(void)
+{
+
+ return mtk_soc_cpuclk;
+}
+
+uint32_t
+mtk_soc_get_timerclk(void)
+{
+
+ return mtk_soc_timerclk;
+}
+
+uint32_t
+mtk_soc_get_socid(void)
+{
+
+ return mtk_soc_socid;
+}
+
+/*
+ * The following are generic reset and clock functions
+ */
+
+/* Default reset time is 100ms */
+#define DEFAULT_RESET_TIME 100000
+
+int
+mtk_soc_reset_device(device_t dev)
+{
+ int res;
+
+ res = fdt_reset_assert_all(dev);
+ if (res == 0) {
+ DELAY(DEFAULT_RESET_TIME);
+ res = fdt_reset_deassert_all(dev);
+ if (res == 0)
+ DELAY(DEFAULT_RESET_TIME);
+ }
+
+ return (res);
+}
+
+int
+mtk_soc_stop_clock(device_t dev)
+{
+
+ return (fdt_clock_disable_all(dev));
+}
+
+int
+mtk_soc_start_clock(device_t dev)
+{
+
+ return (fdt_clock_enable_all(dev));
+}
+
+int
+mtk_soc_assert_reset(device_t dev)
+{
+
+ return (fdt_reset_assert_all(dev));
+}
+
+int
+mtk_soc_deassert_reset(device_t dev)
+{
+
+ return (fdt_reset_deassert_all(dev));
+}
+
+void
+mtk_soc_reset(void)
+{
+
+ mtk_sysctl_clr_set(SYSCTL_RSTCTRL, 0, 1);
+ mtk_sysctl_clr_set(SYSCTL_RSTCTRL, 1, 0);
+}
diff --git a/sys/mips/mediatek/mtk_soc.h b/sys/mips/mediatek/mtk_soc.h
new file mode 100644
index 0000000..445675d
--- /dev/null
+++ b/sys/mips/mediatek/mtk_soc.h
@@ -0,0 +1,130 @@
+/*-
+ * Copyright (c) 2016 Stanislav Galabov.
+ * 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 unmodified, this list of conditions, and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MTK_SOC_H_
+#define _MTK_SOC_H_
+
+enum mtk_soc_id {
+ MTK_SOC_UNKNOWN,
+ MTK_SOC_RT3050,
+ MTK_SOC_RT3052,
+ MTK_SOC_RT3350,
+ MTK_SOC_RT3352,
+ MTK_SOC_RT3662,
+ MTK_SOC_RT3883,
+ MTK_SOC_RT5350,
+ MTK_SOC_MT7620A,
+ MTK_SOC_MT7620N,
+ MTK_SOC_MT7621,
+ MTK_SOC_MT7628,
+ MTK_SOC_MT7688,
+ MTK_SOC_MAX
+};
+
+#define RT305X_CPU_CLKSEL_OFF 18
+#define RT305X_CPU_CLKSEL_MSK 0x1
+#define RT3352_CPU_CLKSEL_OFF 8
+#define RT3352_CPU_CLKSEL_MSK 0x1
+#define RT3883_CPU_CLKSEL_OFF 8
+#define RT3883_CPU_CLKSEL_MSK 0x3
+#define RT5350_CPU_CLKSEL_OFF1 8
+#define RT5350_CPU_CLKSEL_OFF2 10
+#define RT5350_CPU_CLKSEL_MSK 0x1
+#define MT7628_CPU_CLKSEL_OFF 6
+#define MT7628_CPU_CLKSEL_MSK 0x1
+
+#define MT7620_CPU_CLK_AUX0 (1u<<24)
+#define MT7620_CPLL_SW_CFG (1u<<31)
+#define MT7620_PLL_MULT_RATIO_OFF 16
+#define MT7620_PLL_MULT_RATIO_MSK 0x7
+#define MT7620_PLL_MULT_RATIO_BASE 24
+#define MT7620_PLL_DIV_RATIO_OFF 10
+#define MT7620_PLL_DIV_RATIO_MSK 0x3
+#define MT7620_PLL_DIV_RATIO_BASE 2
+#define MT7620_PLL_DIV_RATIO_MAX 8
+#define MT7620_XTAL_40 40
+
+#define MT7621_USES_MEMDIV (1u<<30)
+#define MT7621_MEMDIV_OFF 4
+#define MT7621_MEMDIV_MSK 0x7f
+#define MT7621_MEMDIV_BASE 1
+#define MT7621_CLKSEL_OFF 6
+#define MT7621_CLKSEL_MSK 0x7
+#define MT7621_CLKSEL_25MHZ_VAL 6
+#define MT7621_CLKSEL_20MHZ_VAL 3
+#define MT7621_CLKSEL_20MHZ 20
+#define MT7621_CLKSEL_25MHZ 25
+#define MT7621_CLK_STS_DIV_OFF 8
+#define MT7621_CLK_STS_MSK 0x1f
+#define MT7621_CLK_STS_BASE 500
+
+#define MTK_MT7621_CLKDIV_REG 0x5648
+#define MTK_MT7621_CLKDIV_OFF 4
+#define MTK_MT7621_CLKDIV_MSK 0x7f
+
+#define MTK_MHZ(x) ((x) * 1000 * 1000)
+
+#define MTK_CPU_CLK_UNKNOWN 0
+#define MTK_CPU_CLK_250MHZ 250000000
+#define MTK_CPU_CLK_300MHZ 300000000
+#define MTK_CPU_CLK_320MHZ 320000000
+#define MTK_CPU_CLK_360MHZ 360000000
+#define MTK_CPU_CLK_384MHZ 384000000
+#define MTK_CPU_CLK_400MHZ 400000000
+#define MTK_CPU_CLK_480MHZ 480000000
+#define MTK_CPU_CLK_500MHZ 500000000
+#define MTK_CPU_CLK_575MHZ 575000000
+#define MTK_CPU_CLK_580MHZ 580000000
+#define MTK_CPU_CLK_600MHZ 600000000
+#define MTK_CPU_CLK_880MHZ 880000000
+
+#define MTK_UART_CLK_40MHZ 40000000
+#define MTK_UART_CLK_50MHZ 50000000
+
+#define MTK_UARTDIV_2 2
+#define MTK_UARTDIV_3 3
+
+#define MTK_DEFAULT_BASE 0x10000000
+#define MTK_MT7621_BASE 0x1e000000
+#define MTK_DEFAULT_SIZE 0x6000
+
+extern void mtk_soc_try_early_detect(void);
+extern uint32_t mtk_soc_get_uartclk(void);
+extern uint32_t mtk_soc_get_cpuclk(void);
+extern uint32_t mtk_soc_get_timerclk(void);
+extern uint32_t mtk_soc_get_socid(void);
+
+extern int mtk_soc_reset_device(device_t);
+extern int mtk_soc_stop_clock(device_t);
+extern int mtk_soc_start_clock(device_t);
+extern int mtk_soc_assert_reset(device_t);
+extern int mtk_soc_deassert_reset(device_t);
+extern void mtk_soc_reset(void);
+
+#endif /* _MTK_SOC_H_ */
diff --git a/sys/mips/mediatek/mtk_spi_v1.c b/sys/mips/mediatek/mtk_spi_v1.c
new file mode 100644
index 0000000..1835b8e
--- /dev/null
+++ b/sys/mips/mediatek/mtk_spi_v1.c
@@ -0,0 +1,351 @@
+/*-
+ * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org>
+ * Copyright (c) 2011, Aleksandr Rybalko <ray@FreeBSD.org>
+ * Copyright (c) 2013, Alexander A. Mityaev <sansan@adm.ua>
+ * 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 unmodified, this list of conditions, and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+//#include <machine/pmap.h>
+
+#include <dev/spibus/spi.h>
+#include <dev/spibus/spibusvar.h>
+#include "spibus_if.h"
+
+#include "opt_platform.h"
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <mips/mediatek/mtk_spi_v1.h>
+#include <dev/flash/mx25lreg.h>
+
+#undef MTK_SPI_DEBUG
+#ifdef MTK_SPI_DEBUG
+#define dprintf printf
+#else
+#define dprintf(x, arg...)
+#endif
+
+/*
+ * register space access macros
+ */
+#define SPI_WRITE(sc, reg, val) do { \
+ bus_write_4(sc->sc_mem_res, (reg), (val)); \
+ } while (0)
+
+#define SPI_READ(sc, reg) bus_read_4(sc->sc_mem_res, (reg))
+
+#define SPI_SET_BITS(sc, reg, bits) \
+ SPI_WRITE(sc, reg, SPI_READ(sc, (reg)) | (bits))
+
+#define SPI_CLEAR_BITS(sc, reg, bits) \
+ SPI_WRITE(sc, reg, SPI_READ(sc, (reg)) & ~(bits))
+
+struct mtk_spi_softc {
+ device_t sc_dev;
+ struct resource *sc_mem_res;
+};
+
+static int mtk_spi_probe(device_t);
+static int mtk_spi_attach(device_t);
+static int mtk_spi_detach(device_t);
+static int mtk_spi_wait(struct mtk_spi_softc *);
+static void mtk_spi_chip_activate(struct mtk_spi_softc *);
+static void mtk_spi_chip_deactivate(struct mtk_spi_softc *);
+static uint8_t mtk_spi_txrx(struct mtk_spi_softc *, uint8_t *, int);
+static int mtk_spi_transfer(device_t, device_t, struct spi_command *);
+static phandle_t mtk_spi_get_node(device_t, device_t);
+
+static struct ofw_compat_data compat_data[] = {
+ { "ralink,rt2880-spi", 1 },
+ { "ralink,rt3050-spi", 1 },
+ { "ralink,rt3352-spi", 1 },
+ { "ralink,rt3883-spi", 1 },
+ { "ralink,rt5350-spi", 1 },
+ { "ralink,mt7620a-spi", 1 },
+ { NULL, 0 }
+};
+
+static int
+mtk_spi_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 SPI Controller (v1)");
+
+ return (0);
+}
+
+static int
+mtk_spi_attach(device_t dev)
+{
+ struct mtk_spi_softc *sc = device_get_softc(dev);
+ int rid;
+
+ sc->sc_dev = dev;
+ rid = 0;
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_mem_res) {
+ device_printf(dev, "Could not map memory\n");
+ return (ENXIO);
+ }
+
+ if (mtk_spi_wait(sc)) {
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+ return (EBUSY);
+ }
+
+ SPI_WRITE(sc, MTK_SPICFG, MSBFIRST | SPICLKPOL | TX_ON_CLK_FALL |
+ SPI_CLK_DIV8); /* XXX: make it configurable */
+ /*
+ * W25Q64CV max 104MHz, bus 120-192 MHz, so divide by 2.
+ * Update: divide by 4, DEV2 to fast for flash.
+ */
+
+ device_add_child(dev, "spibus", 0);
+ return (bus_generic_attach(dev));
+}
+
+static int
+mtk_spi_detach(device_t dev)
+{
+ struct mtk_spi_softc *sc = device_get_softc(dev);
+
+ SPI_SET_BITS(sc, MTK_SPICTL, HIZSMOSI | CS_HIGH);
+
+ if (sc->sc_mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+
+ return (0);
+}
+
+static void
+mtk_spi_chip_activate(struct mtk_spi_softc *sc)
+{
+// printf("%s\n", __func__);
+ mtk_spi_wait(sc);
+ /*
+ * Put all CSx to low
+ */
+ SPI_CLEAR_BITS(sc, MTK_SPICTL, CS_HIGH | HIZSMOSI);
+}
+
+static void
+mtk_spi_chip_deactivate(struct mtk_spi_softc *sc)
+{
+// printf("%s\n", __func__);
+ mtk_spi_wait(sc);
+ /*
+ * Put all CSx to high
+ */
+ SPI_SET_BITS(sc, MTK_SPICTL, CS_HIGH | HIZSMOSI);
+}
+
+static int
+mtk_spi_wait(struct mtk_spi_softc *sc)
+{
+ int i = 1000;
+
+ while (i--) {
+ if (!SPI_READ(sc, MTK_SPIBUSY))
+ break;
+ }
+ if (i == 0) {
+ printf("busy\n");
+ return (1);
+ }
+
+ return (0);
+}
+
+static uint8_t
+mtk_spi_txrx(struct mtk_spi_softc *sc, uint8_t *data, int write)
+{
+
+ if (mtk_spi_wait(sc))
+ return (EBUSY);
+
+ if (write == MTK_SPI_WRITE) {
+ SPI_WRITE(sc, MTK_SPIDATA, *data);
+ SPI_SET_BITS(sc, MTK_SPICTL, START_WRITE);
+ //printf("%s(W:%d)\n", __func__, *data);
+ } else {/* MTK_SPI_READ */
+ SPI_SET_BITS(sc, MTK_SPICTL, START_READ);
+ if (mtk_spi_wait(sc))
+ return (EBUSY);
+
+ *data = SPI_READ(sc, MTK_SPIDATA) & 0xff;
+ //printf("%s(R:%d)\n", __func__, *data);
+ }
+ return (0);
+}
+
+static int
+mtk_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
+{
+ struct mtk_spi_softc *sc;
+ uint8_t *buf, byte, *tx_buf;
+ struct spibus_ivar *devi = SPIBUS_IVAR(child);
+ int i, sz, error = 0, write = 0;
+
+ sc = device_get_softc(dev);
+
+ if (devi->cs != 0)
+ /* Only 1 CS */
+ return (ENXIO);
+
+ /* There is always a command to transfer. */
+ tx_buf = (uint8_t *)(cmd->tx_cmd);
+
+ /* Perform some fixup because MTK dont support duplex SPI */
+ switch(tx_buf[0]) {
+ case CMD_READ_IDENT:
+ cmd->tx_cmd_sz = 1;
+ cmd->rx_cmd_sz = 3;
+ break;
+ case CMD_ENTER_4B_MODE:
+ case CMD_EXIT_4B_MODE:
+ case CMD_WRITE_ENABLE:
+ case CMD_WRITE_DISABLE:
+ cmd->tx_cmd_sz = 1;
+ cmd->rx_cmd_sz = 0;
+ break;
+ case CMD_READ_STATUS:
+ cmd->tx_cmd_sz = 1;
+ cmd->rx_cmd_sz = 1;
+ break;
+ case CMD_READ:
+ case CMD_FAST_READ:
+ cmd->rx_cmd_sz = cmd->tx_data_sz = 0;
+ break;
+ case CMD_SECTOR_ERASE:
+ cmd->rx_cmd_sz = 0;
+ break;
+ case CMD_PAGE_PROGRAM:
+ cmd->rx_cmd_sz = cmd->rx_data_sz = 0;
+ break;
+ }
+
+ mtk_spi_chip_activate(sc);
+
+ if (cmd->tx_cmd_sz + cmd->rx_cmd_sz) {
+ buf = (uint8_t *)(cmd->rx_cmd);
+ tx_buf = (uint8_t *)(cmd->tx_cmd);
+ sz = cmd->tx_cmd_sz + cmd->rx_cmd_sz;
+
+ for (i = 0; i < sz; i++) {
+ if(i < cmd->tx_cmd_sz) {
+ byte = tx_buf[i];
+ error = mtk_spi_txrx(sc, &byte,
+ MTK_SPI_WRITE);
+ if (error)
+ goto mtk_spi_transfer_fail;
+ continue;
+ }
+ error = mtk_spi_txrx(sc, &byte,
+ MTK_SPI_READ);
+ if (error)
+ goto mtk_spi_transfer_fail;
+ buf[i] = byte;
+ }
+ }
+
+ /*
+ * Transfer/Receive data
+ */
+
+ if (cmd->tx_data_sz + cmd->rx_data_sz) {
+ write = (cmd->tx_data_sz > 0)?1:0;
+ buf = (uint8_t *)(write ? cmd->tx_data : cmd->rx_data);
+ sz = write ? cmd->tx_data_sz : cmd->rx_data_sz;
+
+ for (i = 0; i < sz; i++) {
+ byte = buf[i];
+ error = mtk_spi_txrx(sc, &byte,
+ write ? MTK_SPI_WRITE : MTK_SPI_READ);
+ if (error)
+ goto mtk_spi_transfer_fail;
+ buf[i] = byte;
+ }
+ }
+mtk_spi_transfer_fail:
+ mtk_spi_chip_deactivate(sc);
+
+ return (error);
+}
+
+static phandle_t
+mtk_spi_get_node(device_t bus, device_t dev)
+{
+
+ /* We only have one child, the SPI bus, which needs our own node. */
+ return (ofw_bus_get_node(bus));
+}
+
+static device_method_t mtk_spi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, mtk_spi_probe),
+ DEVMETHOD(device_attach, mtk_spi_attach),
+ DEVMETHOD(device_detach, mtk_spi_detach),
+
+ DEVMETHOD(spibus_transfer, mtk_spi_transfer),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, mtk_spi_get_node),
+
+ DEVMETHOD_END
+};
+
+static driver_t mtk_spi_driver = {
+ .name = "spi",
+ .methods = mtk_spi_methods,
+ .size = sizeof(struct mtk_spi_softc),
+};
+
+static devclass_t mtk_spi_devclass;
+
+DRIVER_MODULE(mtk_spi_v1, simplebus, mtk_spi_driver, mtk_spi_devclass, 0, 0);
diff --git a/sys/mips/mediatek/mtk_spi_v1.h b/sys/mips/mediatek/mtk_spi_v1.h
new file mode 100644
index 0000000..3b82e5b
--- /dev/null
+++ b/sys/mips/mediatek/mtk_spi_v1.h
@@ -0,0 +1,71 @@
+/*-
+ * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org>
+ * Copyright (c) 2011, Aleksandr Rybalko <ray@FreeBSD.org>
+ * Copyright (c) 2013, Alexander A. Mityaev <sansan@adm.ua>
+ * Copyright (c) 2016, Stanislav Galabov <sgalabov@gmail.com>
+ * 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 unmodified, this list of conditions, and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MTK_SPIVAR_H_
+#define _MTK_SPIVAR_H_
+
+/* SPI controller interface */
+
+#define MTK_SPISTAT 0x00
+/* SPIBUSY is alias for SPIBUSY, because SPISTAT have only BUSY bit*/
+#define MTK_SPIBUSY MTK_SPISTAT
+
+#define MTK_SPICFG 0x10
+#define MSBFIRST (1<<8)
+#define SPICLKPOL (1<<6)
+#define CAPT_ON_CLK_FALL (1<<5)
+#define TX_ON_CLK_FALL (1<<4)
+#define HIZSPI (1<<3) /* Set SPI pins to Tri-state */
+#define SPI_CLK_SHIFT 0 /* SPI clock divide control */
+#define SPI_CLK_MASK 0x00000007
+#define SPI_CLK_DIV2 0
+#define SPI_CLK_DIV4 1
+#define SPI_CLK_DIV8 2
+#define SPI_CLK_DIV16 3
+#define SPI_CLK_DIV32 4
+#define SPI_CLK_DIV64 5
+#define SPI_CLK_DIV128 6
+#define SPI_CLK_DISABLED 7
+
+#define MTK_SPICTL 0x14
+#define HIZSMOSI (1<<3)
+#define START_WRITE (1<<2)
+#define START_READ (1<<1)
+#define CS_HIGH (1<<0)
+
+#define MTK_SPIDATA 0x20
+#define SPIDATA_MASK 0x000000ff
+
+#define MTK_SPI_WRITE 1
+#define MTK_SPI_READ 0
+
+#endif /* _MTK_SPIVAR_H_ */
diff --git a/sys/mips/mediatek/mtk_spi_v2.c b/sys/mips/mediatek/mtk_spi_v2.c
new file mode 100644
index 0000000..0ffd976
--- /dev/null
+++ b/sys/mips/mediatek/mtk_spi_v2.c
@@ -0,0 +1,357 @@
+/*-
+ * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org>
+ * Copyright (c) 2011, Aleksandr Rybalko <ray@FreeBSD.org>
+ * Copyright (c) 2013, Alexander A. Mityaev <sansan@adm.ua>
+ * Copyright (c) 2016, Stanislav Galabov <sgalabov@gmail.com>
+ * 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 unmodified, this list of conditions, and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+//#include <machine/pmap.h>
+
+#include <dev/spibus/spi.h>
+#include <dev/spibus/spibusvar.h>
+#include "spibus_if.h"
+
+#include "opt_platform.h"
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <mips/mediatek/mtk_spi_v2.h>
+#include <dev/flash/mx25lreg.h>
+
+#undef MTK_SPI_DEBUG
+#ifdef MTK_SPI_DEBUG
+#define dprintf printf
+#else
+#define dprintf(x, arg...)
+#endif
+
+/*
+ * register space access macros
+ */
+#define SPI_WRITE(sc, reg, val) do { \
+ bus_write_4(sc->sc_mem_res, (reg), (val)); \
+ } while (0)
+
+#define SPI_READ(sc, reg) bus_read_4(sc->sc_mem_res, (reg))
+
+#define SPI_SET_BITS(sc, reg, bits) \
+ SPI_WRITE(sc, reg, SPI_READ(sc, (reg)) | (bits))
+
+#define SPI_CLEAR_BITS(sc, reg, bits) \
+ SPI_WRITE(sc, reg, SPI_READ(sc, (reg)) & ~(bits))
+
+struct mtk_spi_softc {
+ device_t sc_dev;
+ struct resource *sc_mem_res;
+};
+
+static int mtk_spi_probe(device_t);
+static int mtk_spi_attach(device_t);
+static int mtk_spi_detach(device_t);
+static int mtk_spi_wait(struct mtk_spi_softc *);
+static void mtk_spi_chip_activate(struct mtk_spi_softc *);
+static void mtk_spi_chip_deactivate(struct mtk_spi_softc *);
+static uint8_t mtk_spi_txrx(struct mtk_spi_softc *, uint8_t *, int);
+static int mtk_spi_transfer(device_t, device_t, struct spi_command *);
+static phandle_t mtk_spi_get_node(device_t, device_t);
+
+static struct ofw_compat_data compat_data[] = {
+ { "ralink,mt7621-spi", 1 },
+ { "ralink,mtk7628an-spi", 1 },
+ { NULL, 0 }
+};
+
+static int
+mtk_spi_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 SPI Controller (v2)");
+
+ return (0);
+}
+
+static int
+mtk_spi_attach(device_t dev)
+{
+ struct mtk_spi_softc *sc = device_get_softc(dev);
+ uint32_t val;
+ int rid;
+
+ sc->sc_dev = dev;
+ rid = 0;
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_mem_res) {
+ device_printf(dev, "Could not map memory\n");
+ return (ENXIO);
+ }
+
+ if (mtk_spi_wait(sc)) {
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+ return (EBUSY);
+ }
+
+ val = SPI_READ(sc, MTK_SPIMASTER);
+ val &= ~(0xfff << 16);
+ val |= 13 << 16;
+ val |= 7 << 29;
+ val |= 1 << 2;
+ SPI_WRITE(sc, MTK_SPIMASTER, val);
+ /*
+ * W25Q64CV max 104MHz, bus 120-192 MHz, so divide by 2.
+ * Update: divide by 4, DEV2 to fast for flash.
+ */
+
+ device_add_child(dev, "spibus", 0);
+ return (bus_generic_attach(dev));
+}
+
+static int
+mtk_spi_detach(device_t dev)
+{
+ struct mtk_spi_softc *sc = device_get_softc(dev);
+
+ //SPI_SET_BITS(sc, MTK_SPICTL, HIZSMOSI | CS_HIGH);
+
+ if (sc->sc_mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+
+ return (0);
+}
+
+static void
+mtk_spi_chip_activate(struct mtk_spi_softc *sc)
+{
+// printf("%s\n", __func__);
+ mtk_spi_wait(sc);
+ /*
+ * Put all CSx to low
+ */
+ SPI_SET_BITS(sc, MTK_SPIPOLAR, 1);
+}
+
+static void
+mtk_spi_chip_deactivate(struct mtk_spi_softc *sc)
+{
+// printf("%s\n", __func__);
+ mtk_spi_wait(sc);
+ /*
+ * Put all CSx to high
+ */
+ SPI_CLEAR_BITS(sc, MTK_SPIPOLAR, 1);
+}
+
+static int
+mtk_spi_wait(struct mtk_spi_softc *sc)
+{
+ int i = 1000;
+
+ while (i--) {
+ if (!(SPI_READ(sc, MTK_SPITRANS) & SPIBUSY))
+ break;
+ }
+ if (i == 0) {
+ //printf("busy\n");
+ return (1);
+ }
+
+ return (0);
+}
+
+static uint8_t
+mtk_spi_txrx(struct mtk_spi_softc *sc, uint8_t *data, int write)
+{
+
+ if (mtk_spi_wait(sc))
+ return (0xff);
+
+ if (write == MTK_SPI_WRITE) {
+ SPI_WRITE(sc, MTK_SPIOPCODE, (*data));
+ SPI_WRITE(sc, MTK_SPIMOREBUF, (8<<24));
+ } else {
+ SPI_WRITE(sc, MTK_SPIMOREBUF, (8<<12));
+ }
+
+ SPI_SET_BITS(sc, MTK_SPITRANS, SPISTART);
+
+ if (mtk_spi_wait(sc))
+ return (0xff);
+
+ if (write == MTK_SPI_READ) {
+ *data = SPI_READ(sc, MTK_SPIDATA) & 0xff;
+ }
+
+ return (0);
+}
+
+static int
+mtk_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
+{
+ struct mtk_spi_softc *sc;
+ uint8_t *buf, byte, *tx_buf;
+ struct spibus_ivar *devi = SPIBUS_IVAR(child);
+ int i, sz, error, write = 0;
+
+ sc = device_get_softc(dev);
+
+ if (devi->cs != 0)
+ /* Only 1 CS */
+ return (ENXIO);
+
+ /* There is always a command to transfer. */
+ tx_buf = (uint8_t *)(cmd->tx_cmd);
+
+ /* Perform some fixup because MTK dont support duplex SPI */
+ switch(tx_buf[0]) {
+ case CMD_READ_IDENT:
+ cmd->tx_cmd_sz = 1;
+ cmd->rx_cmd_sz = 3;
+ break;
+ case CMD_ENTER_4B_MODE:
+ case CMD_EXIT_4B_MODE:
+ case CMD_WRITE_ENABLE:
+ case CMD_WRITE_DISABLE:
+ cmd->tx_cmd_sz = 1;
+ cmd->rx_cmd_sz = 0;
+ break;
+ case CMD_READ_STATUS:
+ cmd->tx_cmd_sz = 1;
+ cmd->rx_cmd_sz = 1;
+ break;
+ case CMD_READ:
+ case CMD_FAST_READ:
+ cmd->rx_cmd_sz = cmd->tx_data_sz = 0;
+ break;
+ case CMD_SECTOR_ERASE:
+ cmd->rx_cmd_sz = 0;
+ break;
+ case CMD_PAGE_PROGRAM:
+ cmd->rx_cmd_sz = cmd->rx_data_sz = 0;
+ break;
+ }
+
+ mtk_spi_chip_activate(sc);
+
+ if (cmd->tx_cmd_sz + cmd->rx_cmd_sz) {
+ buf = (uint8_t *)(cmd->rx_cmd);
+ tx_buf = (uint8_t *)(cmd->tx_cmd);
+ sz = cmd->tx_cmd_sz + cmd->rx_cmd_sz;
+
+ for (i = 0; i < sz; i++) {
+ if(i < cmd->tx_cmd_sz) {
+ byte = tx_buf[i];
+ error = mtk_spi_txrx(sc, &byte,
+ MTK_SPI_WRITE);
+ if (error)
+ goto mtk_spi_transfer_fail;
+ continue;
+ }
+ error = mtk_spi_txrx(sc, &byte,
+ MTK_SPI_READ);
+ if (error)
+ goto mtk_spi_transfer_fail;
+ buf[i] = byte;
+ }
+ }
+
+ /*
+ * Transfer/Receive data
+ */
+
+ if (cmd->tx_data_sz + cmd->rx_data_sz) {
+ write = (cmd->tx_data_sz > 0)?1:0;
+ buf = (uint8_t *)(write ? cmd->tx_data : cmd->rx_data);
+ sz = write ? cmd->tx_data_sz : cmd->rx_data_sz;
+
+ for (i = 0; i < sz; i++) {
+ byte = buf[i];
+ error = mtk_spi_txrx(sc, &byte,
+ write ? MTK_SPI_WRITE : MTK_SPI_READ);
+ if (error)
+ goto mtk_spi_transfer_fail;
+ buf[i] = byte;
+ }
+ }
+mtk_spi_transfer_fail:
+ mtk_spi_chip_deactivate(sc);
+
+ return (0);
+}
+
+static phandle_t
+mtk_spi_get_node(device_t bus, device_t dev)
+{
+
+ /* We only have one child, the SPI bus, which needs our own node. */
+ return (ofw_bus_get_node(bus));
+}
+
+static device_method_t mtk_spi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, mtk_spi_probe),
+ DEVMETHOD(device_attach, mtk_spi_attach),
+ DEVMETHOD(device_detach, mtk_spi_detach),
+
+ DEVMETHOD(spibus_transfer, mtk_spi_transfer),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, mtk_spi_get_node),
+
+ DEVMETHOD_END
+};
+
+static driver_t mtk_spi_driver = {
+ .name = "spi",
+ .methods = mtk_spi_methods,
+ .size = sizeof(struct mtk_spi_softc),
+};
+
+static devclass_t mtk_spi_devclass;
+
+DRIVER_MODULE(mtk_spi_v2, simplebus, mtk_spi_driver, mtk_spi_devclass, 0, 0);
diff --git a/sys/mips/mediatek/mtk_spi_v2.h b/sys/mips/mediatek/mtk_spi_v2.h
new file mode 100644
index 0000000..fc7ae31
--- /dev/null
+++ b/sys/mips/mediatek/mtk_spi_v2.h
@@ -0,0 +1,55 @@
+/*-
+ * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org>
+ * Copyright (c) 2011, Aleksandr Rybalko <ray@FreeBSD.org>
+ * Copyright (c) 2013, Alexander A. Mityaev <sansan@adm.ua>
+ * Copyright (c) 2016, Stanislav Galabov <sgalabov@gmail.com>
+ * 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 unmodified, this list of conditions, and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MTK_SPI_NEWVAR_H_
+#define _MTK_SPI_NEWVAR_H_
+
+/* SPI controller interface */
+
+#define MTK_SPITRANS 0x00
+#define SPIBUSY (1<<16)
+#define SPISTART (1<<8)
+
+#define MTK_SPIMASTER 0x28
+
+#define MTK_SPIMOREBUF 0x2C
+
+#define MTK_SPIOPCODE 0x04
+#define MTK_SPIDATA 0x08
+#define SPIDATA_MASK 0x000000ff
+
+#define MTK_SPI_WRITE 1
+#define MTK_SPI_READ 0
+
+#define MTK_SPIPOLAR 0x38
+
+#endif /* _MTK_SPI_NEWVAR_H_ */
diff --git a/sys/mips/mediatek/mtk_sysctl.c b/sys/mips/mediatek/mtk_sysctl.c
new file mode 100644
index 0000000..c16b55d
--- /dev/null
+++ b/sys/mips/mediatek/mtk_sysctl.c
@@ -0,0 +1,191 @@
+/*-
+ * Copyright (c) 2016 Stanislav Galabov.
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/interrupt.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/malloc.h>
+
+#include <machine/fdt.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <mips/mediatek/mtk_sysctl.h>
+
+#include <dev/fdt/fdt_common.h>
+
+struct mtk_sysctl_softc {
+ device_t dev;
+ struct resource *mem_res;
+ int mem_rid;
+ struct mtx mtx;
+};
+
+static struct mtk_sysctl_softc *mtk_sysctl_sc = NULL;
+
+static struct ofw_compat_data compat_data[] = {
+ { "ralink,rt2880-sysc", 1 },
+ { "ralink,rt3050-sysc", 1 },
+ { "ralink,rt3352-sysc", 1 },
+ { "ralink,rt3883-sysc", 1 },
+ { "ralink,rt5350-sysc", 1 },
+ { "ralink,mt7620a-sysc", 1 },
+ { "mtk,mt7621-sysc", 1 },
+
+ /* Sentinel */
+ { NULL, 0 }
+};
+
+#define MTK_SYSCTL_LOCK(sc) mtx_lock_spin(&(sc)->mtx)
+#define MTK_SYSCTL_UNLOCK(sc) mtx_unlock_spin(&(sc)->mtx)
+#define MTK_SYSCTL_LOCK_INIT(sc) \
+ mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \
+ "mtk_sysctl", MTX_SPIN)
+#define MTK_SYSCTL_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx)
+
+static int
+mtk_sysctl_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 System Controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int mtk_sysctl_detach(device_t);
+
+static int
+mtk_sysctl_attach(device_t dev)
+{
+ struct mtk_sysctl_softc *sc = device_get_softc(dev);
+
+ if (device_get_unit(dev) != 0 || mtk_sysctl_sc != NULL) {
+ device_printf(dev, "Only one sysctl module supported\n");
+ return (ENXIO);
+ }
+
+ mtk_sysctl_sc = sc;
+
+ /* Map control/status registers. */
+ sc->mem_rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->mem_rid, RF_ACTIVE);
+
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "couldn't map memory\n");
+ mtk_sysctl_detach(dev);
+ return (ENXIO);
+ }
+
+ sc->dev = dev;
+
+ MTK_SYSCTL_LOCK_INIT(sc);
+
+ return (0);
+}
+
+static int
+mtk_sysctl_detach(device_t dev)
+{
+ struct mtk_sysctl_softc *sc = device_get_softc(dev);
+
+ if (sc->mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid,
+ sc->mem_res);
+
+ MTK_SYSCTL_LOCK_DESTROY(sc);
+
+ return(0);
+}
+
+uint32_t
+mtk_sysctl_get(uint32_t reg)
+{
+ uint32_t val;
+
+ MTK_SYSCTL_LOCK(mtk_sysctl_sc);
+ val = bus_read_4(mtk_sysctl_sc->mem_res, reg);
+ MTK_SYSCTL_UNLOCK(mtk_sysctl_sc);
+
+ return (val);
+}
+
+void
+mtk_sysctl_set(uint32_t reg, uint32_t val)
+{
+
+ MTK_SYSCTL_LOCK(mtk_sysctl_sc);
+ bus_write_4(mtk_sysctl_sc->mem_res, reg, val);
+ MTK_SYSCTL_UNLOCK(mtk_sysctl_sc);
+}
+
+void
+mtk_sysctl_clr_set(uint32_t reg, uint32_t clr, uint32_t set)
+{
+ uint32_t val;
+
+ MTK_SYSCTL_LOCK(mtk_sysctl_sc);
+ val = bus_read_4(mtk_sysctl_sc->mem_res, reg);
+ val &= ~(clr);
+ val |= set;
+ bus_write_4(mtk_sysctl_sc->mem_res, reg, val);
+ MTK_SYSCTL_UNLOCK(mtk_sysctl_sc);
+}
+
+static device_method_t mtk_sysctl_methods[] = {
+ DEVMETHOD(device_probe, mtk_sysctl_probe),
+ DEVMETHOD(device_attach, mtk_sysctl_attach),
+ DEVMETHOD(device_detach, mtk_sysctl_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t mtk_sysctl_driver = {
+ "sysc",
+ mtk_sysctl_methods,
+ sizeof(struct mtk_sysctl_softc),
+};
+static devclass_t mtk_sysctl_devclass;
+
+EARLY_DRIVER_MODULE(mtk_sysctl, simplebus, mtk_sysctl_driver,
+ mtk_sysctl_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_EARLY);
diff --git a/sys/mips/mediatek/mtk_sysctl.h b/sys/mips/mediatek/mtk_sysctl.h
new file mode 100644
index 0000000..0ef908e
--- /dev/null
+++ b/sys/mips/mediatek/mtk_sysctl.h
@@ -0,0 +1,59 @@
+/*-
+ * Copyright (c) 2016 Stanislav Galabov.
+ * 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 unmodified, this list of conditions, and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MTK_SYSCTL_H_
+#define _MTK_SYSCTL_H_
+
+/* System Control */
+#define SYSCTL_CHIPID0_3 0x00
+#define SYSCTL_CHIPID4_7 0x04
+
+#define SYSCTL_REVID 0x0C
+#define SYSCTL_REVID_MASK 0xFFFF
+#define SYSCTL_MT7621_REV_E 0x0101
+
+#define SYSCTL_SYSCFG 0x10
+#define SYSCTL_SYSCFG1 0x14
+#define SYSCTL_CLKCFG0 0x2C
+#define SYSCTL_CLKCFG1 0x30
+#define SYSCTL_RSTCTRL 0x34
+#define SYSCTL_GPIOMODE 0x60
+
+#define SYSCTL_CUR_CLK_STS 0x44
+
+#define SYSCTL_MT7620_CPLL_CFG0 0x54
+#define SYSCTL_MT7620_CPLL_CFG1 0x58
+
+#define SYSCFG1_USB_HOST_MODE (1<<10)
+
+extern uint32_t mtk_sysctl_get(uint32_t);
+extern void mtk_sysctl_set(uint32_t, uint32_t);
+extern void mtk_sysctl_clr_set(uint32_t, uint32_t, uint32_t);
+
+#endif /* _MTK_SYSCTL_H_ */
diff --git a/sys/mips/mediatek/mtk_usb_phy.c b/sys/mips/mediatek/mtk_usb_phy.c
new file mode 100644
index 0000000..d400720
--- /dev/null
+++ b/sys/mips/mediatek/mtk_usb_phy.c
@@ -0,0 +1,324 @@
+/*-
+ * Copyright (c) 2016 Stanislav Galabov.
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+
+#include <machine/bus.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/fdt/fdt_clock.h>
+#include <mips/mediatek/fdt_reset.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <mips/mediatek/mtk_sysctl.h>
+#include <mips/mediatek/mtk_soc.h>
+#include <mips/mediatek/mtk_usb_phy.h>
+
+#define RESET_ASSERT_DELAY 1000
+#define RESET_DEASSERT_DELAY 10000
+
+struct mtk_usb_phy_softc {
+ device_t dev;
+ struct resource * res;
+ uint32_t fm_base;
+ uint32_t u2_base;
+ uint32_t sr_coef;
+ uint32_t socid;
+};
+
+#define USB_PHY_READ(_sc, _off) bus_read_4((_sc)->res, (_off))
+#define USB_PHY_WRITE(_sc, _off, _val) bus_write_4((_sc)->res, (_off), (_val))
+#define USB_PHY_CLR_SET(_sc, _off, _clr, _set) \
+ USB_PHY_WRITE(_sc, _off, ((USB_PHY_READ(_sc, _off) & ~(_clr)) | (_set)))
+
+#define USB_PHY_READ_U2(_sc, _off) \
+ USB_PHY_READ((_sc), ((_sc)->u2_base + (_off)))
+#define USB_PHY_WRITE_U2(_sc, _off, _val) \
+ USB_PHY_WRITE((_sc), ((_sc)->u2_base + (_off)), (_val))
+#define USB_PHY_CLR_SET_U2(_sc, _off, _clr, _set) \
+ USB_PHY_WRITE_U2((_sc), (_off), ((USB_PHY_READ_U2((_sc), (_off)) & \
+ ~(_clr)) | (_set)))
+#define USB_PHY_BARRIER(_sc) bus_barrier((_sc)->res, 0, 0, \
+ BUS_SPACE_BARRIER_WRITE | BUS_SPACE_BARRIER_READ)
+
+#define USB_PHY_READ_FM(_sc, _off) \
+ USB_PHY_READ((_sc), ((_sc)->fm_base + (_off)))
+#define USB_PHY_WRITE_FM(_sc, _off) \
+ USB_PHY_WRITE((_sc), ((_sc)->fm_base + (_off)), (_val))
+#define USB_PHY_CLR_SET_FM(_sc, _off, _clr, _set) \
+ USB_PHY_WRITE_U2((_sc), (_off), ((USB_PHY_READ_U2((_sc), (_off)) & \
+ ~(_clr)) | (_set)))
+
+static void mtk_usb_phy_mt7621_init(device_t);
+static void mtk_usb_phy_mt7628_init(device_t);
+
+static struct ofw_compat_data compat_data[] = {
+ { "ralink,mt7620a-usbphy", MTK_SOC_MT7620A },
+ { "ralink,mt7628an-usbphy", MTK_SOC_MT7628 },
+ { "ralink,rt3xxx-usbphy", MTK_SOC_RT3352 },
+ { NULL, MTK_SOC_UNKNOWN }
+};
+
+static int
+mtk_usb_phy_probe(device_t dev)
+{
+ struct mtk_usb_phy_softc *sc = device_get_softc(dev);
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+ if ((sc->socid =
+ ofw_bus_search_compatible(dev, compat_data)->ocd_data) ==
+ MTK_SOC_UNKNOWN)
+ return (ENXIO);
+
+ device_set_desc(dev, "MTK USB PHY");
+
+ return (0);
+}
+
+static int
+mtk_usb_phy_attach(device_t dev)
+{
+ struct mtk_usb_phy_softc * sc = device_get_softc(dev);
+ phandle_t node;
+ uint32_t val;
+ int rid;
+
+ sc->dev = dev;
+
+ /* Get our FDT node and SoC id */
+ node = ofw_bus_get_node(dev);
+
+ /* Now let's see about setting USB to host or device mode */
+ /* XXX: is it the same for all SoCs? */
+ val = mtk_sysctl_get(SYSCTL_SYSCFG1);
+ if (OF_hasprop(node, "mtk,usb-device"))
+ val &= ~SYSCFG1_USB_HOST_MODE;
+ else
+ val |= SYSCFG1_USB_HOST_MODE;
+ mtk_sysctl_set(SYSCTL_SYSCFG1, val);
+
+ /* If we have clocks defined - enable them */
+ if (OF_hasprop(node, "clocks"))
+ fdt_clock_enable_all(dev);
+
+ /* If we have resets defined - perform a reset sequence */
+ if (OF_hasprop(node, "resets")) {
+ fdt_reset_assert_all(dev);
+ DELAY(RESET_ASSERT_DELAY);
+ fdt_reset_deassert_all(dev);
+ DELAY(RESET_DEASSERT_DELAY);
+ }
+
+ /* Careful, some devices actually require resources */
+ if (OF_hasprop(node, "reg")) {
+ rid = 0;
+ sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->res == NULL) {
+ device_printf(dev, "could not map memory\n");
+ return (ENXIO);
+ }
+ } else {
+ sc->res = NULL;
+ }
+
+ /* Some SoCs require specific USB PHY init... handle these */
+ switch (sc->socid) {
+ case MTK_SOC_MT7628: /* Fallthrough */
+ case MTK_SOC_MT7688:
+ if (sc->res == NULL)
+ return (ENXIO);
+ sc->fm_base = MT7628_FM_FEG_BASE;
+ sc->u2_base = MT7628_U2_BASE;
+ sc->sr_coef = MT7628_SR_COEF;
+ mtk_usb_phy_mt7628_init(dev);
+ break;
+ case MTK_SOC_MT7621:
+ if (sc->res == NULL)
+ return (ENXIO);
+ sc->fm_base = MT7621_FM_FEG_BASE;
+ sc->u2_base = MT7621_U2_BASE;
+ sc->sr_coef = MT7621_SR_COEF;
+ mtk_usb_phy_mt7621_init(dev);
+ break;
+ }
+
+ /* We no longer need the resources, release them */
+ if (sc->res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->res);
+
+ return (0);
+}
+
+static int
+mtk_usb_phy_detach(device_t dev)
+{
+ struct mtk_usb_phy_softc *sc = device_get_softc(dev);
+ phandle_t node;
+
+ /* Get our FDT node */
+ node = ofw_bus_get_node(dev);
+
+ /* If we have resets defined - assert them */
+ if (OF_hasprop(node, "resets"))
+ fdt_reset_assert_all(dev);
+
+ /* If we have clocks defined - disable them */
+ if (OF_hasprop(node, "clocks"))
+ fdt_clock_disable_all(dev);
+
+ /* Finally, release resources, if any were allocated */
+ if (sc->res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->res);
+
+ return (0);
+}
+
+/*
+ * Things currently seem to work a lot better without slew rate calibration
+ * both on MT7621 and MT7688, so we leave it out for now.
+ */
+#ifdef notyet
+static void
+mtk_usb_phy_slew_rate_calibration(struct mtk_usb_phy_softc *sc)
+{
+ uint32_t val;
+ int i;
+
+ USB_PHY_CLR_SET_U2(sc, U2_PHY_ACR0, 0, SRCAL_EN);
+ USB_PHY_BARRIER(sc);
+ DELAY(1000);
+
+ USB_PHY_CLR_SET_FM(sc, U2_PHY_FMMONR1, 0, FRCK_EN);
+ USB_PHY_BARRIER(sc);
+ USB_PHY_CLR_SET_FM(sc, U2_PHY_FMCR0, CYCLECNT, 0x400);
+ USB_PHY_BARRIER(sc);
+ USB_PHY_CLR_SET_FM(sc, U2_PHY_FMCR0, 0, FDET_EN);
+ USB_PHY_BARRIER(sc);
+
+ for (i = 0; i < 1000; i++) {
+ if ((val = USB_PHY_READ_FM(sc, U2_PHY_FMMONR0)) != 0) {
+ device_printf(sc->dev, "DONE with FDET\n");
+ break;
+ }
+ DELAY(10000);
+ }
+ device_printf(sc->dev, "After FDET\n");
+
+ USB_PHY_CLR_SET_FM(sc, U2_PHY_FMCR0, FDET_EN, 0);
+ USB_PHY_BARRIER(sc);
+ USB_PHY_CLR_SET_FM(sc, U2_PHY_FMMONR1, FRCK_EN, 0);
+ USB_PHY_BARRIER(sc);
+ USB_PHY_CLR_SET_U2(sc, U2_PHY_ACR0, SRCAL_EN, 0);
+ USB_PHY_BARRIER(sc);
+ DELAY(1000);
+
+ if (val == 0) {
+ USB_PHY_CLR_SET_U2(sc, U2_PHY_ACR0, SRCTRL, 0x4 << SRCTRL_OFF);
+ USB_PHY_BARRIER(sc);
+ } else {
+ val = ((((1024 * 25 * sc->sr_coef) / val) + 500) / 1000) &
+ SRCTRL_MSK;
+ USB_PHY_CLR_SET_U2(sc, U2_PHY_ACR0, SRCTRL, val << SRCTRL_OFF);
+ USB_PHY_BARRIER(sc);
+ }
+}
+#endif
+
+static void
+mtk_usb_phy_mt7621_init(device_t dev)
+{
+#ifdef notyet
+ struct mtk_usb_phy_softc *sc = device_get_softc(dev);
+
+ /* Slew rate calibration only, but for 2 ports */
+ mtk_usb_phy_slew_rate_calibration(sc);
+
+ sc->u2_base = MT7621_U2_BASE_P1;
+ mtk_usb_phy_slew_rate_calibration(sc);
+#endif
+}
+
+static void
+mtk_usb_phy_mt7628_init(device_t dev)
+{
+ struct mtk_usb_phy_softc *sc = device_get_softc(dev);
+
+ /* XXX: possibly add barriers between the next writes? */
+ USB_PHY_WRITE_U2(sc, U2_PHY_DCR0, 0x00ffff02);
+ USB_PHY_BARRIER(sc);
+ USB_PHY_WRITE_U2(sc, U2_PHY_DCR0, 0x00555502);
+ USB_PHY_BARRIER(sc);
+ USB_PHY_WRITE_U2(sc, U2_PHY_DCR0, 0x00aaaa02);
+ USB_PHY_BARRIER(sc);
+ USB_PHY_WRITE_U2(sc, U2_PHY_DCR0, 0x00000402);
+ USB_PHY_BARRIER(sc);
+ USB_PHY_WRITE_U2(sc, U2_PHY_AC0, 0x0048086a);
+ USB_PHY_BARRIER(sc);
+ USB_PHY_WRITE_U2(sc, U2_PHY_AC1, 0x4400001c);
+ USB_PHY_BARRIER(sc);
+ USB_PHY_WRITE_U2(sc, U2_PHY_ACR3, 0xc0200000);
+ USB_PHY_BARRIER(sc);
+ USB_PHY_WRITE_U2(sc, U2_PHY_DTM0, 0x02000000);
+ USB_PHY_BARRIER(sc);
+
+#ifdef notyet
+ /* Slew rate calibration */
+ mtk_usb_phy_slew_rate_calibration(sc);
+#endif
+}
+
+static device_method_t mtk_usb_phy_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, mtk_usb_phy_probe),
+ DEVMETHOD(device_attach, mtk_usb_phy_attach),
+ DEVMETHOD(device_detach, mtk_usb_phy_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ DEVMETHOD_END
+};
+
+static driver_t mtk_usb_phy_driver = {
+ .name = "usbphy",
+ .methods = mtk_usb_phy_methods,
+ .size = sizeof(struct mtk_usb_phy_softc),
+};
+
+static devclass_t mtk_usb_phy_devclass;
+
+DRIVER_MODULE(usbphy, simplebus, mtk_usb_phy_driver, mtk_usb_phy_devclass, 0,
+ 0);
diff --git a/sys/mips/mediatek/mtk_usb_phy.h b/sys/mips/mediatek/mtk_usb_phy.h
new file mode 100644
index 0000000..24fb661
--- /dev/null
+++ b/sys/mips/mediatek/mtk_usb_phy.h
@@ -0,0 +1,66 @@
+/*-
+ * Copyright (c) 2016 Stanislav Galabov.
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+#ifndef _MTK_USB_PHY_H_
+#define _MTK_USB_PHY_H_
+
+#define MT7621_FM_FEG_BASE 0x0100
+#define MT7621_U2_BASE 0x0800
+#define MT7621_U2_BASE_P1 0x1000
+#define MT7621_SR_COEF 28
+
+#define MT7628_FM_FEG_BASE 0x0f00
+#define MT7628_U2_BASE 0x0800
+#define MT7628_SR_COEF 32
+
+#define U2_PHY_AC0 0x00
+#define U2_PHY_AC1 0x04
+#define U2_PHY_AC2 0x08
+#define U2_PHY_ACR0 0x10
+#define SRCAL_EN (1<<23)
+#define SRCTRL_MSK 0x7
+#define SRCTRL_OFF 16
+#define SRCTRL (SRCTRL_MSK<<SRCTRL_OFF)
+#define U2_PHY_ACR1 0x14
+#define U2_PHY_ACR2 0x18
+#define U2_PHY_ACR3 0x1C
+
+#define U2_PHY_DCR0 0x60
+#define U2_PHY_DCR1 0x64
+#define U2_PHY_DTM0 0x68
+#define U2_PHY_DTM1 0x6C
+
+#define U2_PHY_FMCR0 0x00
+#define CYCLECNT (0xffffff)
+#define FDET_EN (1<<24)
+#define U2_PHY_FMCR1 0x04
+#define FRCK_EN (1<<8)
+#define U2_PHY_FMCR2 0x08
+#define U2_PHY_FMMONR0 0x0C
+#define U2_PHY_FMMONR1 0x10
+
+#endif /* _MTK_USB_PHY_H_ */
diff --git a/sys/mips/mediatek/mtk_xhci.c b/sys/mips/mediatek/mtk_xhci.c
new file mode 100644
index 0000000..0899307
--- /dev/null
+++ b/sys/mips/mediatek/mtk_xhci.c
@@ -0,0 +1,298 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2015 Stanislav Galabov. All rights reserved.
+ * Copyright (c) 2010,2011 Aleksandr Rybalko. All rights reserved.
+ * Copyright (c) 2007-2008 Hans Petter Selasky. 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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 <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+#include <sys/rman.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+
+#include <dev/usb/controller/xhci.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#define XHCI_HC_DEVSTR "MTK USB 3.0 controller"
+
+static device_probe_t mtk_xhci_fdt_probe;
+static device_attach_t mtk_xhci_fdt_attach;
+static device_detach_t mtk_xhci_fdt_detach;
+
+static void mtk_xhci_fdt_init(device_t dev);
+
+static int
+mtk_xhci_fdt_probe(device_t self)
+{
+
+ if (!ofw_bus_status_okay(self))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(self, "mtk,usb-xhci"))
+ return (ENXIO);
+
+ device_set_desc(self, XHCI_HC_DEVSTR);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+mtk_xhci_fdt_attach(device_t self)
+{
+ struct xhci_softc *sc = device_get_softc(self);
+ int err;
+ int rid;
+
+ /* initialise some bus fields */
+ sc->sc_bus.parent = self;
+ sc->sc_bus.devices = sc->sc_devices;
+ sc->sc_bus.devices_max = XHCI_MAX_DEVICES;
+ sc->sc_bus.dma_bits = 32;
+
+ rid = 0;
+ sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_io_res) {
+ device_printf(self, "Could not map memory\n");
+ goto error;
+ }
+ sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
+ sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
+ sc->sc_io_size = rman_get_size(sc->sc_io_res);
+
+ mtk_xhci_fdt_init(self);
+
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->sc_irq_res == NULL) {
+ device_printf(self, "Could not allocate irq\n");
+ goto error;
+ }
+
+ sc->sc_bus.bdev = device_add_child(self, "usbus", -1);
+ if (!(sc->sc_bus.bdev)) {
+ device_printf(self, "Could not add USB device\n");
+ goto error;
+ }
+ device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+ device_set_desc(sc->sc_bus.bdev, XHCI_HC_DEVSTR);
+
+ sprintf(sc->sc_vendor, "Mediatek");
+
+ err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, (driver_intr_t *)xhci_interrupt, sc, &sc->sc_intr_hdl);
+ if (err) {
+ device_printf(self, "Could not setup irq, %d\n", err);
+ sc->sc_intr_hdl = NULL;
+ goto error;
+ }
+
+ err = xhci_init(sc, self, 1);
+ if (err == 0)
+ err = xhci_halt_controller(sc);
+ if (err == 0)
+ err = xhci_start_controller(sc);
+ if (err == 0)
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+ if (err) {
+ device_printf(self, "USB init failed err=%d\n", err);
+ goto error;
+ }
+ return (0);
+
+error:
+ mtk_xhci_fdt_detach(self);
+ return (ENXIO);
+}
+
+static int
+mtk_xhci_fdt_detach(device_t self)
+{
+ struct xhci_softc *sc = device_get_softc(self);
+ device_t bdev;
+ int err;
+
+ if (sc->sc_bus.bdev) {
+ bdev = sc->sc_bus.bdev;
+ device_detach(bdev);
+ device_delete_child(self, bdev);
+ }
+ /* during module unload there are lots of children leftover */
+ device_delete_children(self);
+
+ if (sc->sc_irq_res && sc->sc_intr_hdl) {
+ /*
+ * only call xhci_detach() after xhci_init()
+ */
+ xhci_uninit(sc);
+
+ err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl);
+ if (err)
+ device_printf(self, "Could not tear down irq, %d\n",
+ err);
+ sc->sc_intr_hdl = NULL;
+ }
+ if (sc->sc_irq_res) {
+ bus_release_resource(self, SYS_RES_IRQ, 0,
+ sc->sc_irq_res);
+ sc->sc_irq_res = NULL;
+ }
+ if (sc->sc_io_res) {
+ bus_release_resource(self, SYS_RES_MEMORY, 0,
+ sc->sc_io_res);
+ sc->sc_io_res = NULL;
+ }
+
+ return (0);
+}
+
+static device_method_t mtk_xhci_fdt_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, mtk_xhci_fdt_probe),
+ DEVMETHOD(device_attach, mtk_xhci_fdt_attach),
+ DEVMETHOD(device_detach, mtk_xhci_fdt_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ DEVMETHOD_END
+};
+
+static driver_t mtk_xhci_fdt_driver = {
+ .name = "xhci",
+ .methods = mtk_xhci_fdt_methods,
+ .size = sizeof(struct xhci_softc),
+};
+
+static devclass_t mtk_xhci_fdt_devclass;
+
+DRIVER_MODULE(xhci, simplebus, mtk_xhci_fdt_driver, mtk_xhci_fdt_devclass, 0,
+ 0);
+
+#define USB_HDMA_CFG 0x950
+#define USB_HDMA_CFG_MT7621_VAL 0x10E0E0C
+
+#define U3_LTSSM_TIMING_PARAM3 0x2514
+#define U3_LTSSM_TIMING_VAL 0x3E8012C
+
+#define SYNC_HS_EOF 0x938
+#define SYNC_HS_EOF_VAL 0x201F3
+
+#define USB_IP_SPAR0 0x107C8
+#define USB_IP_SPAR0_VAL 1
+
+#define U2_PHY_BASE_P0 0x10800
+#define U2_PHY_BASE_P1 0x11000
+#define U2_PHYD_CR1 0x64
+#define U2_PHYD_CR1_MASK (3<<18)
+#define U2_PHYD_CR1_VAL (1<<18)
+
+#define USB_IP_PW_CTRL 0x10700
+#define USB_IP_PW_CTRL_1 0x10704
+#define USB_IP_CAP 0x10724
+#define USB_U3_CTRL(p) (0x10730 + ((p) * 0x08))
+#define USB_U2_CTRL(p) (0x10750 + ((p) * 0x08))
+
+#define USB_IP_SW_RST (1 << 0)
+#define USB_IP_PDN (1 << 0)
+
+#define USB_PORT_DIS (1 << 0)
+#define USB_PORT_PDN (1 << 1)
+
+#define U3_PORT_NUM(p) (p & 0xFF)
+#define U2_PORT_NUM(p) ((p>>8) & 0xFF)
+
+#define RD4(_sc, _reg) bus_read_4((_sc)->sc_io_res, (_reg))
+#define WR4(_sc, _reg, _val) bus_write_4((_sc)->sc_io_res, (_reg), (_val))
+#define CLRSET4(_sc, _reg, _clr, _set) \
+ WR4((_sc), (_reg), (RD4((_sc), (_reg)) & ~(_clr)) | (_set))
+
+static void
+mtk_xhci_fdt_init(device_t dev)
+{
+ struct xhci_softc *sc;
+ uint32_t temp, u3_ports, u2_ports, i;
+
+ sc = device_get_softc(dev);
+
+ temp = RD4(sc, USB_IP_CAP);
+ u3_ports = U3_PORT_NUM(temp);
+ u2_ports = U2_PORT_NUM(temp);
+
+ device_printf(dev, "%d USB3 ports, %d USB2 ports\n",
+ u3_ports, u2_ports);
+
+ CLRSET4(sc, USB_IP_PW_CTRL, 0, USB_IP_SW_RST);
+ CLRSET4(sc, USB_IP_PW_CTRL, USB_IP_SW_RST, 0);
+ CLRSET4(sc, USB_IP_PW_CTRL_1, USB_IP_PDN, 0);
+
+ for (i = 0; i < u3_ports; i++)
+ CLRSET4(sc, USB_U3_CTRL(i), USB_PORT_PDN | USB_PORT_DIS, 0);
+
+ for (i = 0; i < u2_ports; i++)
+ CLRSET4(sc, USB_U2_CTRL(i), USB_PORT_PDN | USB_PORT_DIS, 0);
+
+ DELAY(100000);
+
+ WR4(sc, USB_HDMA_CFG, USB_HDMA_CFG_MT7621_VAL);
+ WR4(sc, U3_LTSSM_TIMING_PARAM3, U3_LTSSM_TIMING_VAL);
+ WR4(sc, SYNC_HS_EOF, SYNC_HS_EOF_VAL);
+ WR4(sc, USB_IP_SPAR0, USB_IP_SPAR0_VAL);
+ CLRSET4(sc, U2_PHY_BASE_P0 + U2_PHYD_CR1, U2_PHYD_CR1_MASK,
+ U2_PHYD_CR1_VAL);
+ CLRSET4(sc, U2_PHY_BASE_P1 + U2_PHYD_CR1, U2_PHYD_CR1_MASK,
+ U2_PHYD_CR1_VAL);
+}
diff --git a/sys/mips/mediatek/uart_dev_mtk.c b/sys/mips/mediatek/uart_dev_mtk.c
new file mode 100644
index 0000000..5993cb6
--- /dev/null
+++ b/sys/mips/mediatek/uart_dev_mtk.c
@@ -0,0 +1,552 @@
+/* $NetBSD: uart.c,v 1.2 2007/03/23 20:05:47 dogcow Exp $ */
+
+/*-
+ * Copyright (c) 2013, Alexander A. Mityaev <sansan@adm.ua>
+ * Copyright (c) 2010 Aleksandr Rybalko.
+ * Copyright (c) 2007 Ruslan Ermilov and Vsevolod Lobko.
+ * Copyright (c) 2007 Oleksandr Tymoshenko.
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_ddb.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kdb.h>
+#include <sys/reboot.h>
+#include <sys/sysctl.h>
+#include <sys/kernel.h>
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#include <dev/uart/uart.h>
+#include <dev/uart/uart_cpu.h>
+#include <dev/uart/uart_cpu_fdt.h>
+#include <dev/uart/uart_bus.h>
+
+#include <mips/mediatek/uart_dev_mtk.h>
+#include <mips/mediatek/mtk_soc.h>
+#include <mips/mediatek/mtk_sysctl.h>
+
+#include "uart_if.h"
+
+/* Set some reference clock value. Real value will be taken from FDT */
+#define DEFAULT_RCLK (120 * 1000 * 1000)
+
+/*
+ * Low-level UART interface.
+ */
+static int mtk_uart_probe(struct uart_bas *bas);
+static void mtk_uart_init(struct uart_bas *bas, int, int, int, int);
+static void mtk_uart_term(struct uart_bas *bas);
+static void mtk_uart_putc(struct uart_bas *bas, int);
+static int mtk_uart_rxready(struct uart_bas *bas);
+static int mtk_uart_getc(struct uart_bas *bas, struct mtx *);
+
+static struct uart_ops uart_mtk_ops = {
+ .probe = mtk_uart_probe,
+ .init = mtk_uart_init,
+ .term = mtk_uart_term,
+ .putc = mtk_uart_putc,
+ .rxready = mtk_uart_rxready,
+ .getc = mtk_uart_getc,
+};
+
+static int uart_output = 1;
+TUNABLE_INT("kern.uart_output", &uart_output);
+SYSCTL_INT(_kern, OID_AUTO, uart_output, CTLFLAG_RW,
+ &uart_output, 0, "UART output enabled.");
+
+static int
+mtk_uart_probe(struct uart_bas *bas)
+{
+ return (0);
+}
+
+static void
+mtk_uart_init(struct uart_bas *bas, int baudrate, int databits,
+ int stopbits, int parity)
+{
+ /* CLKDIV = 384000000/ 3/ 16/ br */
+ /* for 384MHz CLKDIV = 8000000 / baudrate; */
+ switch (databits) {
+ case 5:
+ databits = UART_LCR_5B;
+ break;
+ case 6:
+ databits = UART_LCR_6B;
+ break;
+ case 7:
+ databits = UART_LCR_7B;
+ break;
+ case 8:
+ databits = UART_LCR_8B;
+ break;
+ default:
+ /* Unsupported */
+ return;
+ }
+ switch (parity) {
+ case UART_PARITY_EVEN: parity = (UART_LCR_PEN|UART_LCR_EVEN); break;
+ case UART_PARITY_ODD: parity = (UART_LCR_PEN); break;
+ case UART_PARITY_NONE: parity = 0; break;
+ /* Unsupported */
+ default: return;
+ }
+
+ if (bas->rclk && baudrate) {
+ uart_setreg(bas, UART_CDDL_REG, bas->rclk/16/baudrate);
+ uart_barrier(bas);
+ }
+
+ uart_setreg(bas, UART_LCR_REG, databits |
+ (stopbits==1?0:UART_LCR_STB_15) |
+ parity);
+ uart_barrier(bas);
+}
+
+static void
+mtk_uart_term(struct uart_bas *bas)
+{
+ uart_setreg(bas, UART_MCR_REG, 0);
+ uart_barrier(bas);
+}
+
+static void
+mtk_uart_putc(struct uart_bas *bas, int c)
+{
+ char chr;
+ if (!uart_output) return;
+ chr = c;
+ while (!(uart_getreg(bas, UART_LSR_REG) & UART_LSR_THRE));
+ uart_setreg(bas, UART_TX_REG, c);
+ uart_barrier(bas);
+ while (!(uart_getreg(bas, UART_LSR_REG) & UART_LSR_THRE));
+}
+
+static int
+mtk_uart_rxready(struct uart_bas *bas)
+{
+ if (uart_getreg(bas, UART_LSR_REG) & UART_LSR_DR)
+ return (1);
+ return (0);
+}
+
+static int
+mtk_uart_getc(struct uart_bas *bas, struct mtx *hwmtx)
+{
+ int c;
+
+ uart_lock(hwmtx);
+
+ while (!(uart_getreg(bas, UART_LSR_REG) & UART_LSR_DR)) {
+ uart_unlock(hwmtx);
+ DELAY(10);
+ uart_lock(hwmtx);
+ }
+
+ c = uart_getreg(bas, UART_RX_REG);
+
+ uart_unlock(hwmtx);
+
+ return (c);
+}
+
+/*
+ * High-level UART interface.
+ */
+struct uart_mtk_softc {
+ struct uart_softc base;
+ uint8_t ier_mask;
+ uint8_t ier;
+};
+
+static int mtk_uart_bus_attach(struct uart_softc *);
+static int mtk_uart_bus_detach(struct uart_softc *);
+static int mtk_uart_bus_flush(struct uart_softc *, int);
+static int mtk_uart_bus_getsig(struct uart_softc *);
+static int mtk_uart_bus_ioctl(struct uart_softc *, int, intptr_t);
+static int mtk_uart_bus_ipend(struct uart_softc *);
+static int mtk_uart_bus_param(struct uart_softc *, int, int, int, int);
+static int mtk_uart_bus_probe(struct uart_softc *);
+static int mtk_uart_bus_receive(struct uart_softc *);
+static int mtk_uart_bus_setsig(struct uart_softc *, int);
+static int mtk_uart_bus_transmit(struct uart_softc *);
+static void mtk_uart_bus_grab(struct uart_softc *);
+static void mtk_uart_bus_ungrab(struct uart_softc *);
+
+static kobj_method_t uart_mtk_methods[] = {
+ KOBJMETHOD(uart_attach, mtk_uart_bus_attach),
+ KOBJMETHOD(uart_detach, mtk_uart_bus_detach),
+ KOBJMETHOD(uart_flush, mtk_uart_bus_flush),
+ KOBJMETHOD(uart_getsig, mtk_uart_bus_getsig),
+ KOBJMETHOD(uart_ioctl, mtk_uart_bus_ioctl),
+ KOBJMETHOD(uart_ipend, mtk_uart_bus_ipend),
+ KOBJMETHOD(uart_param, mtk_uart_bus_param),
+ KOBJMETHOD(uart_probe, mtk_uart_bus_probe),
+ KOBJMETHOD(uart_receive, mtk_uart_bus_receive),
+ KOBJMETHOD(uart_setsig, mtk_uart_bus_setsig),
+ KOBJMETHOD(uart_transmit, mtk_uart_bus_transmit),
+ KOBJMETHOD(uart_grab, mtk_uart_bus_grab),
+ KOBJMETHOD(uart_ungrab, mtk_uart_bus_ungrab),
+ { 0, 0 }
+};
+
+struct uart_class uart_mtk_class = {
+ "uart_mtk",
+ uart_mtk_methods,
+ sizeof(struct uart_mtk_softc),
+ .uc_ops = &uart_mtk_ops,
+ .uc_range = 1, /* use hinted range */
+ .uc_rclk = 0
+};
+
+static struct ofw_compat_data compat_data[] = {
+ { "ralink,rt2880-uart", (uintptr_t)&uart_mtk_class },
+ { "ralink,rt3050-uart", (uintptr_t)&uart_mtk_class },
+ { "ralink,rt3352-uart", (uintptr_t)&uart_mtk_class },
+ { "ralink,rt3883-uart", (uintptr_t)&uart_mtk_class },
+ { "ralink,rt5350-uart", (uintptr_t)&uart_mtk_class },
+ { "ralink,mt7620a-uart", (uintptr_t)&uart_mtk_class },
+ { NULL, (uintptr_t)NULL },
+};
+UART_FDT_CLASS_AND_DEVICE(compat_data);
+
+
+#define SIGCHG(c, i, s, d) \
+ if (c) { \
+ i |= (i & s) ? s : s | d; \
+ } else { \
+ i = (i & s) ? (i & ~s) | d : i; \
+ }
+
+/*
+ * Disable TX interrupt. uart should be locked
+ */
+static __inline void
+mtk_uart_disable_txintr(struct uart_softc *sc)
+{
+ struct uart_bas *bas = &sc->sc_bas;
+ uint8_t cr;
+
+ cr = uart_getreg(bas, UART_IER_REG);
+ cr &= ~UART_IER_ETBEI;
+ uart_setreg(bas, UART_IER_REG, cr);
+ uart_barrier(bas);
+}
+
+/*
+ * Enable TX interrupt. uart should be locked
+ */
+static __inline void
+mtk_uart_enable_txintr(struct uart_softc *sc)
+{
+ struct uart_bas *bas = &sc->sc_bas;
+ uint8_t cr;
+
+ cr = uart_getreg(bas, UART_IER_REG);
+ cr |= UART_IER_ETBEI;
+ uart_setreg(bas, UART_IER_REG, cr);
+ uart_barrier(bas);
+}
+
+static int
+mtk_uart_bus_attach(struct uart_softc *sc)
+{
+ struct uart_bas *bas;
+ struct uart_devinfo *di;
+ struct uart_mtk_softc *usc = (struct uart_mtk_softc *)sc;
+
+ bas = &sc->sc_bas;
+
+ if (!bas->rclk) {
+ bas->rclk = mtk_soc_get_uartclk();
+ }
+
+ if (sc->sc_sysdev != NULL) {
+ di = sc->sc_sysdev;
+ mtk_uart_init(bas, di->baudrate, di->databits, di->stopbits,
+ di->parity);
+ } else {
+ mtk_uart_init(bas, 57600, 8, 1, 0);
+ }
+
+ sc->sc_rxfifosz = 16;
+ sc->sc_txfifosz = 16;
+
+ (void)mtk_uart_bus_getsig(sc);
+
+ /* Enable FIFO */
+ uart_setreg(bas, UART_FCR_REG,
+ uart_getreg(bas, UART_FCR_REG) |
+ UART_FCR_FIFOEN | UART_FCR_TXTGR_1 | UART_FCR_RXTGR_1);
+ uart_barrier(bas);
+ /* Enable interrupts */
+ usc->ier_mask = 0xf0;
+ uart_setreg(bas, UART_IER_REG,
+ UART_IER_EDSSI | UART_IER_ELSI | UART_IER_ERBFI);
+ uart_barrier(bas);
+
+ return (0);
+}
+
+static int
+mtk_uart_bus_detach(struct uart_softc *sc)
+{
+ return (0);
+}
+
+static int
+mtk_uart_bus_flush(struct uart_softc *sc, int what)
+{
+ struct uart_bas *bas = &sc->sc_bas;
+ uint32_t fcr = uart_getreg(bas, UART_FCR_REG);
+
+ if (what & UART_FLUSH_TRANSMITTER) {
+ uart_setreg(bas, UART_FCR_REG, fcr|UART_FCR_TXRST);
+ uart_barrier(bas);
+ }
+ if (what & UART_FLUSH_RECEIVER) {
+ uart_setreg(bas, UART_FCR_REG, fcr|UART_FCR_RXRST);
+ uart_barrier(bas);
+ }
+ uart_setreg(bas, UART_FCR_REG, fcr);
+ uart_barrier(bas);
+ return (0);
+}
+
+static int
+mtk_uart_bus_getsig(struct uart_softc *sc)
+{
+ uint32_t new, old, sig;
+ uint8_t bes;
+
+ return(0);
+ do {
+ old = sc->sc_hwsig;
+ sig = old;
+ uart_lock(sc->sc_hwmtx);
+ bes = uart_getreg(&sc->sc_bas, UART_MSR_REG);
+ uart_unlock(sc->sc_hwmtx);
+ /* XXX: chip can show delta */
+ SIGCHG(bes & UART_MSR_CTS, sig, SER_CTS, SER_DCTS);
+ SIGCHG(bes & UART_MSR_DCD, sig, SER_DCD, SER_DDCD);
+ SIGCHG(bes & UART_MSR_DSR, sig, SER_DSR, SER_DDSR);
+ new = sig & ~SER_MASK_DELTA;
+ } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
+
+ return (sig);
+}
+
+static int
+mtk_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
+{
+ struct uart_bas *bas;
+ int baudrate, divisor, error;
+
+ bas = &sc->sc_bas;
+ error = 0;
+ uart_lock(sc->sc_hwmtx);
+ switch (request) {
+ case UART_IOCTL_BREAK:
+ /* TODO: Send BREAK */
+ break;
+ case UART_IOCTL_BAUD:
+ divisor = uart_getreg(bas, UART_CDDL_REG);
+ baudrate = bas->rclk / (divisor * 16);
+ *(int*)data = baudrate;
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ uart_unlock(sc->sc_hwmtx);
+ return (error);
+}
+
+static int
+mtk_uart_bus_ipend(struct uart_softc *sc)
+{
+ struct uart_bas *bas;
+ int ipend;
+ uint8_t iir, lsr, msr;
+
+// breakpoint();
+
+ bas = &sc->sc_bas;
+ ipend = 0;
+
+ uart_lock(sc->sc_hwmtx);
+ iir = uart_getreg(&sc->sc_bas, UART_IIR_REG);
+ lsr = uart_getreg(&sc->sc_bas, UART_LSR_REG);
+ uart_setreg(&sc->sc_bas, UART_LSR_REG, lsr);
+ msr = uart_getreg(&sc->sc_bas, UART_MSR_REG);
+ uart_setreg(&sc->sc_bas, UART_MSR_REG, msr);
+ if (iir & UART_IIR_INTP) {
+ uart_unlock(sc->sc_hwmtx);
+ return (0);
+ }
+ switch ((iir >> 1) & 0x07) {
+ case UART_IIR_ID_THRE:
+ ipend |= SER_INT_TXIDLE;
+ break;
+ case UART_IIR_ID_DR2:
+ mtk_uart_bus_flush(sc, UART_FLUSH_RECEIVER);
+ /* passthrough */
+ case UART_IIR_ID_DR:
+ ipend |= SER_INT_RXREADY;
+ break;
+ case UART_IIR_ID_MST:
+ case UART_IIR_ID_LINESTATUS:
+ ipend |= SER_INT_SIGCHG;
+ if (lsr & UART_LSR_BI)
+ ipend |= SER_INT_BREAK;
+ if (lsr & UART_LSR_OE)
+ ipend |= SER_INT_OVERRUN;
+ break;
+ default:
+ /* XXX: maybe return error here */
+ break;
+ }
+
+ uart_unlock(sc->sc_hwmtx);
+
+ return (ipend);
+}
+
+static int
+mtk_uart_bus_param(struct uart_softc *sc, int baudrate, int databits,
+ int stopbits, int parity)
+{
+ uart_lock(sc->sc_hwmtx);
+ mtk_uart_init(&sc->sc_bas, baudrate, databits, stopbits, parity);
+ uart_unlock(sc->sc_hwmtx);
+ return (0);
+}
+
+static int
+mtk_uart_bus_probe(struct uart_softc *sc)
+{
+ int error;
+
+ error = mtk_uart_probe(&sc->sc_bas);
+ if (error)
+ return (error);
+
+ device_set_desc(sc->sc_dev, "MTK UART Controller");
+
+ return (0);
+}
+
+static int
+mtk_uart_bus_receive(struct uart_softc *sc)
+{
+ struct uart_bas *bas;
+ int xc;
+ uint8_t lsr;
+
+ bas = &sc->sc_bas;
+ uart_lock(sc->sc_hwmtx);
+ lsr = uart_getreg(bas, UART_LSR_REG);
+ while ((lsr & UART_LSR_DR)) {
+ if (uart_rx_full(sc)) {
+ sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN;
+ break;
+ }
+ xc = 0;
+ xc = uart_getreg(bas, UART_RX_REG);
+ if (lsr & UART_LSR_FE)
+ xc |= UART_STAT_FRAMERR;
+ if (lsr & UART_LSR_PE)
+ xc |= UART_STAT_PARERR;
+ if (lsr & UART_LSR_OE)
+ xc |= UART_STAT_OVERRUN;
+ uart_barrier(bas);
+ uart_rx_put(sc, xc);
+ lsr = uart_getreg(bas, UART_LSR_REG);
+ }
+
+ uart_unlock(sc->sc_hwmtx);
+ return (0);
+}
+
+static int
+mtk_uart_bus_setsig(struct uart_softc *sc, int sig)
+{
+ /* TODO: implement (?) */
+ return (sig);
+}
+
+static int
+mtk_uart_bus_transmit(struct uart_softc *sc)
+{
+ struct uart_bas *bas = &sc->sc_bas;
+ int i;
+
+ if (!uart_output) return (0);
+
+ bas = &sc->sc_bas;
+ uart_lock(sc->sc_hwmtx);
+ while ((uart_getreg(bas, UART_LSR_REG) & UART_LSR_THRE) == 0);
+ mtk_uart_enable_txintr(sc);
+ for (i = 0; i < sc->sc_txdatasz; i++) {
+ uart_setreg(bas, UART_TX_REG, sc->sc_txbuf[i]);
+ uart_barrier(bas);
+ }
+ sc->sc_txbusy = 1;
+ uart_unlock(sc->sc_hwmtx);
+ return (0);
+}
+
+void
+mtk_uart_bus_grab(struct uart_softc *sc)
+{
+ struct uart_bas *bas = &sc->sc_bas;
+ struct uart_mtk_softc *usc = (struct uart_mtk_softc *)sc;
+
+ uart_lock(sc->sc_hwmtx);
+ usc->ier = uart_getreg(bas, UART_IER_REG);
+ uart_setreg(bas, UART_IER_REG, usc->ier & usc->ier_mask);
+ uart_barrier(bas);
+ uart_unlock(sc->sc_hwmtx);
+}
+
+void
+mtk_uart_bus_ungrab(struct uart_softc *sc)
+{
+ struct uart_mtk_softc *usc = (struct uart_mtk_softc *)sc;
+ struct uart_bas *bas = &sc->sc_bas;
+
+ uart_lock(sc->sc_hwmtx);
+ uart_setreg(bas, UART_IER_REG, usc->ier);
+ uart_barrier(bas);
+ uart_unlock(sc->sc_hwmtx);
+}
diff --git a/sys/mips/mediatek/uart_dev_mtk.h b/sys/mips/mediatek/uart_dev_mtk.h
new file mode 100644
index 0000000..44fcdd8
--- /dev/null
+++ b/sys/mips/mediatek/uart_dev_mtk.h
@@ -0,0 +1,126 @@
+/*-
+ * Copyright (c) 2010 Aleksandr Rybalko.
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS
+ * 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.
+ *
+ * $FreeBSD$
+ */
+#ifndef _MTKUART_H
+#define _MTKUART_H
+
+#undef uart_getreg
+#undef uart_setreg
+#define uart_getreg(bas, reg) \
+ bus_space_read_4((bas)->bst, (bas)->bsh, reg)
+#define uart_setreg(bas, reg, value) \
+ bus_space_write_4((bas)->bst, (bas)->bsh, reg, value)
+
+/* UART registers */
+#define UART_RX_REG 0x00
+#define UART_TX_REG 0x04
+
+#define UART_IER_REG 0x08
+#define UART_IER_EDSSI (1<<3) /* Only full UART */
+#define UART_IER_ELSI (1<<2)
+#define UART_IER_ETBEI (1<<1)
+#define UART_IER_ERBFI (1<<0)
+
+#define UART_IIR_REG 0x0c
+#define UART_IIR_RXFIFO (1<<7)
+#define UART_IIR_TXFIFO (1<<6)
+#define UART_IIR_ID_MST 0
+#define UART_IIR_ID_THRE 1
+#define UART_IIR_ID_DR 2
+#define UART_IIR_ID_LINESTATUS 3
+#define UART_IIR_ID_DR2 6
+#define UART_IIR_ID_SHIFT 1
+#define UART_IIR_ID_MASK 0x0000000e
+#define UART_IIR_INTP (1<<0)
+
+#define UART_FCR_REG 0x10
+#define UART_FCR_RXTGR_1 (0<<6)
+#define UART_FCR_RXTGR_4 (1<<6)
+#define UART_FCR_RXTGR_8 (2<<6)
+#define UART_FCR_RXTGR_12 (3<<6)
+#define UART_FCR_TXTGR_1 (0<<4)
+#define UART_FCR_TXTGR_4 (1<<4)
+#define UART_FCR_TXTGR_8 (2<<4)
+#define UART_FCR_TXTGR_12 (3<<4)
+#define UART_FCR_DMA (1<<3)
+#define UART_FCR_TXRST (1<<2)
+#define UART_FCR_RXRST (1<<1)
+#define UART_FCR_FIFOEN (1<<0)
+
+#define UART_LCR_REG 0x14
+#define UART_LCR_DLAB (1<<7)
+#define UART_LCR_BRK (1<<6)
+#define UART_LCR_FPAR (1<<5)
+#define UART_LCR_EVEN (1<<4)
+#define UART_LCR_PEN (1<<3)
+#define UART_LCR_STB_15 (1<<2)
+#define UART_LCR_5B 0
+#define UART_LCR_6B 1
+#define UART_LCR_7B 2
+#define UART_LCR_8B 3
+
+#define UART_MCR_REG 0x18
+#define UART_MCR_LOOP (1<<4)
+#define UART_MCR_OUT2_L (1<<3) /* Only full UART */
+#define UART_MCR_OUT1_L (1<<2) /* Only full UART */
+#define UART_MCR_RTS_L (1<<1) /* Only full UART */
+#define UART_MCR_DTR_L (1<<0) /* Only full UART */
+
+#define UART_LSR_REG 0x1c
+#define UART_LSR_ERINF (1<<7)
+#define UART_LSR_TEMT (1<<6)
+#define UART_LSR_THRE (1<<5)
+#define UART_LSR_BI (1<<4)
+#define UART_LSR_FE (1<<3)
+#define UART_LSR_PE (1<<2)
+#define UART_LSR_OE (1<<1)
+#define UART_LSR_DR (1<<0)
+
+#define UART_MSR_REG 0x20 /* Only full UART */
+#define UART_MSR_DCD (1<<7) /* Only full UART */
+#define UART_MSR_RI (1<<6) /* Only full UART */
+#define UART_MSR_DSR (1<<5) /* Only full UART */
+#define UART_MSR_CTS (1<<4) /* Only full UART */
+#define UART_MSR_DDCD (1<<3) /* Only full UART */
+#define UART_MSR_TERI (1<<2) /* Only full UART */
+#define UART_MSR_DDSR (1<<1) /* Only full UART */
+#define UART_MSR_DCTS (1<<0) /* Only full UART */
+
+#define UART_CDDL_REG 0x28
+#define UART_CDDLL_REG 0x2c
+#define UART_CDDLH_REG 0x30
+
+#define UART_IFCTL_REG 0x34
+#define UART_IFCTL_IFCTL (1<<0)
+
+int uart_cnattach(void);
+#endif /* _MTKUART_H */
diff --git a/sys/mips/mediatek/uart_dev_mtk_ns8250.c b/sys/mips/mediatek/uart_dev_mtk_ns8250.c
new file mode 100644
index 0000000..3c259ff
--- /dev/null
+++ b/sys/mips/mediatek/uart_dev_mtk_ns8250.c
@@ -0,0 +1,113 @@
+/*-
+ * Copyright (c) 2013 Ian Lepore
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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/conf.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+#include <machine/bus.h>
+
+#include <mips/mediatek/mtk_soc.h>
+#include <mips/mediatek/mtk_sysctl.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/uart/uart.h>
+#include <dev/uart/uart_cpu.h>
+#include <dev/uart/uart_cpu_fdt.h>
+#include <dev/uart/uart_bus.h>
+#include <dev/uart/uart_dev_ns8250.h>
+
+#include "uart_if.h"
+
+/*
+ * High-level UART interface.
+ */
+static struct uart_class uart_mtk_ns8250_class;
+static int mtk_ns8250_bus_probe(struct uart_softc *);
+
+static kobj_method_t mtk_ns8250_methods[] = {
+ KOBJMETHOD(uart_probe, mtk_ns8250_bus_probe),
+
+ KOBJMETHOD(uart_attach, ns8250_bus_attach),
+ KOBJMETHOD(uart_detach, ns8250_bus_detach),
+ KOBJMETHOD(uart_flush, ns8250_bus_flush),
+ KOBJMETHOD(uart_getsig, ns8250_bus_getsig),
+ KOBJMETHOD(uart_ioctl, ns8250_bus_ioctl),
+ KOBJMETHOD(uart_ipend, ns8250_bus_ipend),
+ KOBJMETHOD(uart_param, ns8250_bus_param),
+ KOBJMETHOD(uart_receive, ns8250_bus_receive),
+ KOBJMETHOD(uart_setsig, ns8250_bus_setsig),
+ KOBJMETHOD(uart_transmit, ns8250_bus_transmit),
+ KOBJMETHOD_END
+};
+
+static struct uart_class uart_mtk_ns8250_class = {
+ "mtk8250",
+ mtk_ns8250_methods,
+ sizeof(struct ns8250_softc),
+ .uc_ops = &uart_ns8250_ops,
+ .uc_range = 1, /* use hinted range */
+ .uc_rclk = 0,
+ .uc_rshift = 2
+};
+
+static struct ofw_compat_data compat_data[] = {
+ { "mtk,ns16550a", (uintptr_t)&uart_mtk_ns8250_class },
+ { "ns16550a", (uintptr_t)&uart_mtk_ns8250_class },
+ { NULL, (uintptr_t)NULL },
+};
+UART_FDT_CLASS_AND_DEVICE(compat_data);
+
+static int
+mtk_ns8250_bus_probe(struct uart_softc *sc)
+{
+ int status;
+
+ if (!ofw_bus_status_okay(sc->sc_dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(sc->sc_dev, compat_data)->ocd_data ==
+ (uintptr_t)NULL)
+ return (ENXIO);
+
+ sc->sc_bas.rclk = mtk_soc_get_uartclk();
+
+ status = ns8250_bus_probe(sc);
+ if (status == 0)
+ device_set_desc(sc->sc_dev, "MTK UART Controller (ns16550a)");
+
+ return (status);
+}
diff --git a/sys/mips/mips/db_disasm.c b/sys/mips/mips/db_disasm.c
index 14788ed..108c9dd 100644
--- a/sys/mips/mips/db_disasm.c
+++ b/sys/mips/mips/db_disasm.c
@@ -226,7 +226,7 @@ md_printins(int ins, int mdbdot)
default:
db_printf("\t%s,%s,%s", reg_name[i.RType.rd],
reg_name[i.RType.rs], reg_name[i.RType.rt]);
- };
+ }
break;
case OP_BCOND:
@@ -288,7 +288,7 @@ md_printins(int ins, int mdbdot)
default:
db_printf("%s", c0_opname[i.FRType.func]);
- };
+ }
break;
case OP_COP1:
@@ -323,7 +323,7 @@ md_printins(int ins, int mdbdot)
db_printf("%s.%s\tf%d,f%d,f%d",
cop1_name[i.FRType.func], fmt_name[i.FRType.fmt],
i.FRType.fd, i.FRType.fs, i.FRType.ft);
- };
+ }
break;
case OP_J:
diff --git a/sys/mips/mips/db_trace.c b/sys/mips/mips/db_trace.c
index 445600c..1359a53 100644
--- a/sys/mips/mips/db_trace.c
+++ b/sys/mips/mips/db_trace.c
@@ -260,7 +260,7 @@ loop:
case OP_SYSCALL:
case OP_BREAK:
more = 1; /* stop now */
- };
+ }
break;
case OP_BCOND:
@@ -281,7 +281,7 @@ loop:
case OP_BCx:
case OP_BCy:
more = 2; /* stop after next instruction */
- };
+ }
break;
case OP_SW:
diff --git a/sys/mips/mips/mips_pic.c b/sys/mips/mips/mips_pic.c
index 4e97c41..a24647d 100644
--- a/sys/mips/mips/mips_pic.c
+++ b/sys/mips/mips/mips_pic.c
@@ -71,15 +71,24 @@ __FBSDID("$FreeBSD$");
static int mips_pic_intr(void *);
+struct mips_pic_irqsrc {
+ struct intr_irqsrc isrc;
+ struct resource *res;
+ u_int irq;
+};
+
struct mips_pic_softc {
- device_t pic_dev;
- struct intr_irqsrc * pic_irqs[NREAL_IRQS];
- struct mtx mutex;
- uint32_t nirqs;
+ device_t pic_dev;
+ struct mips_pic_irqsrc pic_irqs[NREAL_IRQS];
+ struct rman pic_irq_rman;
+ struct mtx mutex;
+ uint32_t nirqs;
};
static struct mips_pic_softc *pic_sc;
+#define PIC_INTR_ISRC(sc, irq) (&(sc)->pic_irqs[(irq)].isrc)
+
#ifdef FDT
static struct ofw_compat_data compat_data[] = {
{"mti,cpu-interrupt-controller", true},
@@ -125,13 +134,6 @@ pic_irq_mask(struct mips_pic_softc *sc, u_int irq)
mips_wr_status(mips_rd_status() & ~((1 << irq) << 8));
}
-#ifdef SMP
-static void
-mips_pic_init_secondary(device_t dev)
-{
-}
-#endif /* SMP */
-
static inline intptr_t
pic_xref(device_t dev)
{
@@ -143,6 +145,46 @@ pic_xref(device_t dev)
}
static int
+mips_pic_register_isrcs(struct mips_pic_softc *sc)
+{
+ int error;
+ uint32_t irq, i, tmpirq;
+ struct intr_irqsrc *isrc;
+ char *name;
+
+ for (irq = 0; irq < sc->nirqs; irq++) {
+ sc->pic_irqs[irq].irq = irq;
+ sc->pic_irqs[irq].res = rman_reserve_resource(&sc->pic_irq_rman,
+ irq, irq, 1, RF_ACTIVE, sc->pic_dev);
+ if (sc->pic_irqs[irq].res == NULL) {
+ device_printf(sc->pic_dev,
+ "%s failed to alloc resource for irq %u",
+ __func__, irq);
+ return (ENOMEM);
+ }
+ isrc = PIC_INTR_ISRC(sc, irq);
+ if (irq < NSOFT_IRQS) {
+ name = "sint";
+ tmpirq = irq;
+ } else {
+ name = "int";
+ tmpirq = irq - NSOFT_IRQS;
+ }
+ error = intr_isrc_register(isrc, sc->pic_dev, 0, "%s%u",
+ name, tmpirq);
+ if (error != 0) {
+ for (i = 0; i < irq; i++) {
+ intr_isrc_deregister(PIC_INTR_ISRC(sc, i));
+ }
+ device_printf(sc->pic_dev, "%s failed", __func__);
+ return (error);
+ }
+ }
+
+ return (0);
+}
+
+static int
mips_pic_attach(device_t dev)
{
struct mips_pic_softc *sc;
@@ -162,6 +204,21 @@ mips_pic_attach(device_t dev)
/* Set the number of interrupts */
sc->nirqs = nitems(sc->pic_irqs);
+ /* Init the IRQ rman */
+ sc->pic_irq_rman.rm_type = RMAN_ARRAY;
+ sc->pic_irq_rman.rm_descr = "MIPS PIC IRQs";
+ if (rman_init(&sc->pic_irq_rman) != 0 ||
+ rman_manage_region(&sc->pic_irq_rman, 0, sc->nirqs - 1) != 0) {
+ device_printf(dev, "failed to setup IRQ rman\n");
+ goto cleanup;
+ }
+
+ /* Register the interrupts */
+ if (mips_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.
@@ -174,7 +231,7 @@ mips_pic_attach(device_t dev)
/* Claim our root controller role */
if (intr_pic_claim_root(dev, xref, mips_pic_intr, sc, 0) != 0) {
device_printf(dev, "could not set PIC as a root\n");
- intr_pic_unregister(dev, xref);
+ intr_pic_deregister(dev, xref);
goto cleanup;
}
@@ -189,7 +246,6 @@ mips_pic_intr(void *arg)
{
struct mips_pic_softc *sc = arg;
register_t cause, status;
- struct intr_irqsrc *isrc;
int i, intr;
cause = mips_rd_cause();
@@ -205,15 +261,13 @@ mips_pic_intr(void *arg)
i--; /* Get a 0-offset interrupt. */
intr &= ~(1 << i);
- isrc = sc->pic_irqs[i];
- if (isrc == NULL) {
+ 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;
}
-
- intr_irq_dispatch(isrc, curthread->td_intr_frame);
}
KASSERT(i == 0, ("all interrupts handled"));
@@ -228,178 +282,56 @@ mips_pic_intr(void *arg)
return (FILTER_HANDLED);
}
-static int
-pic_attach_isrc(struct mips_pic_softc *sc, struct intr_irqsrc *isrc, u_int irq)
-{
-
- /*
- * 1. The link between ISRC and controller must be set atomically.
- * 2. Just do things only once in rare case when consumers
- * of shared interrupt came here at the same moment.
- */
- mtx_lock_spin(&sc->mutex);
- if (sc->pic_irqs[irq] != NULL) {
- mtx_unlock_spin(&sc->mutex);
- return (sc->pic_irqs[irq] == isrc ? 0 : EEXIST);
- }
- sc->pic_irqs[irq] = isrc;
- isrc->isrc_data = irq;
- mtx_unlock_spin(&sc->mutex);
-
- if (irq < NSOFT_IRQS)
- intr_irq_set_name(isrc, "sint%u", irq);
- else if (irq < NREAL_IRQS)
- intr_irq_set_name(isrc, "int%u", irq - NSOFT_IRQS);
- else
- panic("Invalid irq %u", irq);
- return (0);
-}
-
-static int
-pic_detach_isrc(struct mips_pic_softc *sc, struct intr_irqsrc *isrc, u_int irq)
+static void
+mips_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
{
+ u_int irq;
- mtx_lock_spin(&sc->mutex);
- if (sc->pic_irqs[irq] != isrc) {
- mtx_unlock_spin(&sc->mutex);
- return (sc->pic_irqs[irq] == NULL ? 0 : EINVAL);
- }
- sc->pic_irqs[irq] = NULL;
- isrc->isrc_data = 0;
- mtx_unlock_spin(&sc->mutex);
-
- intr_irq_set_name(isrc, "%s", "");
- return (0);
+ irq = ((struct mips_pic_irqsrc *)isrc)->irq;
+ pic_irq_mask(device_get_softc(dev), irq);
}
-static int
-pic_irq_from_nspc(struct mips_pic_softc *sc, u_int type, u_int num, u_int *irqp)
+static void
+mips_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
{
+ u_int irq;
- switch (type) {
- case INTR_IRQ_NSPC_PLAIN:
- *irqp = num;
- return (*irqp < sc->nirqs ? 0 : EINVAL);
-
- case INTR_IRQ_NSPC_SWI:
- *irqp = num;
- return (num < NSOFT_IRQS ? 0 : EINVAL);
-
- case INTR_IRQ_NSPC_IRQ:
- *irqp = num + NSOFT_IRQS;
- return (num < NHARD_IRQS ? 0 : EINVAL);
-
- default:
- return (EINVAL);
- }
+ irq = ((struct mips_pic_irqsrc *)isrc)->irq;
+ pic_irq_unmask(device_get_softc(dev), irq);
}
static int
-pic_map_nspc(struct mips_pic_softc *sc, struct intr_irqsrc *isrc, u_int *irqp)
+mips_pic_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
{
- int error;
-
- error = pic_irq_from_nspc(sc, isrc->isrc_nspc_type, isrc->isrc_nspc_num,
- irqp);
- if (error != 0)
- return (error);
- return (pic_attach_isrc(sc, isrc, *irqp));
-}
-
#ifdef FDT
-static int
-pic_map_fdt(struct mips_pic_softc *sc, struct intr_irqsrc *isrc, u_int *irqp)
-{
- u_int irq;
- int error;
+ struct mips_pic_softc *sc;
- irq = isrc->isrc_cells[0];
+ sc = device_get_softc(dev);
- if (irq >= sc->nirqs)
+ if (data == NULL || data->type != INTR_MAP_DATA_FDT ||
+ data->fdt.ncells != 1 || data->fdt.cells[0] >= sc->nirqs)
return (EINVAL);
- error = pic_attach_isrc(sc, isrc, irq);
- if (error != 0)
- return (error);
-
- isrc->isrc_nspc_type = INTR_IRQ_NSPC_PLAIN;
- isrc->isrc_nspc_num = irq;
- isrc->isrc_trig = INTR_TRIGGER_CONFORM;
- isrc->isrc_pol = INTR_POLARITY_CONFORM;
-
- *irqp = irq;
+ *isrcp = PIC_INTR_ISRC(sc, data->fdt.cells[0]);
return (0);
-}
-#endif
-
-static int
-mips_pic_register(device_t dev, struct intr_irqsrc *isrc, boolean_t *is_percpu)
-{
- struct mips_pic_softc *sc = device_get_softc(dev);
- u_int irq;
- int error;
-
- if (isrc->isrc_type == INTR_ISRCT_NAMESPACE)
- error = pic_map_nspc(sc, isrc, &irq);
-#ifdef FDT
- else if (isrc->isrc_type == INTR_ISRCT_FDT)
- error = pic_map_fdt(sc, isrc, &irq);
+#else
+ return (EINVAL);
#endif
- else
- return (EINVAL);
-
- if (error == 0)
- *is_percpu = TRUE;
- return (error);
-}
-
-static void
-mips_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
-{
-
- if (isrc->isrc_trig == INTR_TRIGGER_CONFORM)
- isrc->isrc_trig = INTR_TRIGGER_LEVEL;
-}
-
-static void
-mips_pic_enable_source(device_t dev, struct intr_irqsrc *isrc)
-{
- struct mips_pic_softc *sc = device_get_softc(dev);
- u_int irq = isrc->isrc_data;
-
- pic_irq_unmask(sc, irq);
-}
-
-static void
-mips_pic_disable_source(device_t dev, struct intr_irqsrc *isrc)
-{
- struct mips_pic_softc *sc = device_get_softc(dev);
- u_int irq = isrc->isrc_data;
-
- pic_irq_mask(sc, irq);
-}
-
-static int
-mips_pic_unregister(device_t dev, struct intr_irqsrc *isrc)
-{
- struct mips_pic_softc *sc = device_get_softc(dev);
- u_int irq = isrc->isrc_data;
-
- return (pic_detach_isrc(sc, isrc, irq));
}
static void
mips_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
{
- mips_pic_disable_source(dev, isrc);
+ mips_pic_disable_intr(dev, isrc);
}
static void
mips_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
{
- mips_pic_enable_source(dev, isrc);
+ mips_pic_enable_intr(dev, isrc);
}
static void
@@ -407,19 +339,6 @@ mips_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
{
}
-#ifdef SMP
-static int
-mips_pic_bind(device_t dev, struct intr_irqsrc *isrc)
-{
- return (EOPNOTSUPP);
-}
-
-static void
-mips_pic_ipi_send(device_t dev, struct intr_irqsrc *isrc, cpuset_t cpus)
-{
-}
-#endif
-
static device_method_t mips_pic_methods[] = {
/* Device interface */
#ifndef FDT
@@ -427,20 +346,15 @@ static device_method_t mips_pic_methods[] = {
#endif
DEVMETHOD(device_probe, mips_pic_probe),
DEVMETHOD(device_attach, mips_pic_attach),
+
/* Interrupt controller interface */
- DEVMETHOD(pic_disable_source, mips_pic_disable_source),
+ DEVMETHOD(pic_disable_intr, mips_pic_disable_intr),
DEVMETHOD(pic_enable_intr, mips_pic_enable_intr),
- DEVMETHOD(pic_enable_source, mips_pic_enable_source),
- DEVMETHOD(pic_post_filter, mips_pic_post_filter),
- DEVMETHOD(pic_post_ithread, mips_pic_post_ithread),
+ DEVMETHOD(pic_map_intr, mips_pic_map_intr),
DEVMETHOD(pic_pre_ithread, mips_pic_pre_ithread),
- DEVMETHOD(pic_register, mips_pic_register),
- DEVMETHOD(pic_unregister, mips_pic_unregister),
-#ifdef SMP
- DEVMETHOD(pic_bind, mips_pic_bind),
- DEVMETHOD(pic_init_secondary, mips_pic_init_secondary),
- DEVMETHOD(pic_ipi_send, mips_pic_ipi_send),
-#endif
+ DEVMETHOD(pic_post_ithread, mips_pic_post_ithread),
+ DEVMETHOD(pic_post_filter, mips_pic_post_filter),
+
{ 0, 0 }
};
@@ -469,7 +383,6 @@ void
cpu_establish_hardintr(const char *name, driver_filter_t *filt,
void (*handler)(void*), void *arg, int irq, int flags, void **cookiep)
{
- u_int vec;
int res;
/*
@@ -479,17 +392,11 @@ cpu_establish_hardintr(const char *name, driver_filter_t *filt,
panic("%s called for unknown hard intr %d", __func__, irq);
KASSERT(pic_sc != NULL, ("%s: no pic", __func__));
- vec = intr_namespace_map_irq(pic_sc->pic_dev, INTR_IRQ_NSPC_IRQ, irq);
- KASSERT(vec != NIRQ, ("Unable to map hard IRQ %d\n", irq));
- res = intr_irq_add_handler(pic_sc->pic_dev, filt, handler, arg, vec,
- flags, cookiep);
+ irq += NSOFT_IRQS;
+ res = intr_setup_irq(pic_sc->pic_dev, pic_sc->pic_irqs[irq].res, filt,
+ handler, arg, flags, cookiep);
if (res != 0) panic("Unable to add hard IRQ %d handler", irq);
-
- (void)pic_irq_from_nspc(pic_sc, INTR_IRQ_NSPC_IRQ, irq, &vec);
- KASSERT(pic_sc->pic_irqs[vec] != NULL,
- ("Hard IRQ %d not registered\n", irq));
- intr_irq_set_name(pic_sc->pic_irqs[vec], "%s", name);
}
void
@@ -497,23 +404,15 @@ cpu_establish_softintr(const char *name, driver_filter_t *filt,
void (*handler)(void*), void *arg, int irq, int flags,
void **cookiep)
{
- u_int vec;
int res;
if (irq < 0 || irq > NSOFT_IRQS)
panic("%s called for unknown soft intr %d", __func__, irq);
KASSERT(pic_sc != NULL, ("%s: no pic", __func__));
- vec = intr_namespace_map_irq(pic_sc->pic_dev, INTR_IRQ_NSPC_SWI, irq);
- KASSERT(vec <= NIRQ, ("Unable to map soft IRQ %d\n", irq));
- intr_irq_add_handler(pic_sc->pic_dev, filt, handler, arg, vec,
- flags, cookiep);
+ res = intr_setup_irq(pic_sc->pic_dev, pic_sc->pic_irqs[irq].res, filt,
+ handler, arg, flags, cookiep);
if (res != 0) panic("Unable to add soft IRQ %d handler", irq);
-
- (void)pic_irq_from_nspc(pic_sc, INTR_IRQ_NSPC_SWI, irq, &vec);
- KASSERT(pic_sc->pic_irqs[vec] != NULL,
- ("Soft IRQ %d not registered\n", irq));
- intr_irq_set_name(pic_sc->pic_irqs[vec], "%s", name);
}
diff --git a/sys/mips/mips/nexus.c b/sys/mips/mips/nexus.c
index 89049f0..a2a8e527 100644
--- a/sys/mips/mips/nexus.c
+++ b/sys/mips/mips/nexus.c
@@ -457,14 +457,11 @@ static int
nexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags,
driver_filter_t *filt, driver_intr_t *intr, void *arg, void **cookiep)
{
- int irq;
#ifdef MIPS_INTRNG
- for (irq = rman_get_start(res); irq <= rman_get_end(res); irq++) {
- intr_irq_add_handler(child, filt, intr, arg, irq, flags,
- cookiep);
- }
+ return (intr_setup_irq(child, res, filt, intr, arg, flags, cookiep));
#else
+ int irq;
register_t s;
s = intr_disable();
@@ -477,8 +474,9 @@ nexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags,
cpu_establish_hardintr(device_get_nameunit(child), filt, intr, arg,
irq, flags, cookiep);
intr_restore(s);
-#endif
+
return (0);
+#endif
}
static int
@@ -486,7 +484,7 @@ nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih)
{
#ifdef MIPS_INTRNG
- return (intr_irq_remove_handler(child, rman_get_start(r), ih));
+ return (intr_teardown_irq(child, r, ih));
#else
printf("Unimplemented %s at %s:%d\n", __func__, __FILE__, __LINE__);
return (0);
@@ -499,7 +497,8 @@ nexus_config_intr(device_t dev, int irq, enum intr_trigger trig,
enum intr_polarity pol)
{
- return (intr_irq_config(irq, trig, pol));
+ device_printf(dev, "bus_config_intr is obsolete and not supported!\n");
+ return (EOPNOTSUPP);
}
static int
@@ -507,7 +506,7 @@ nexus_describe_intr(device_t dev, device_t child, struct resource *irq,
void *cookie, const char *descr)
{
- return (intr_irq_describe(rman_get_start(irq), cookie, descr));
+ return (intr_describe_irq(child, irq, cookie, descr));
}
#ifdef SMP
@@ -515,7 +514,7 @@ static int
nexus_bind_intr(device_t dev, device_t child, struct resource *irq, int cpu)
{
- return (intr_irq_bind(rman_get_start(irq), cpu));
+ return (intr_bind_irq(child, irq, cpu));
}
#endif
diff --git a/sys/mips/mips/ofw_machdep.c b/sys/mips/mips/ofw_machdep.c
index 26bc260..6b840e7 100644
--- a/sys/mips/mips/ofw_machdep.c
+++ b/sys/mips/mips/ofw_machdep.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2015 Ian Lepore <ian@freebsd.org>
- * All rights excluded.
+ * All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
OpenPOWER on IntegriCloud