diff options
Diffstat (limited to 'sys/mips')
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 |