From 06763f5e55540ddfba295f8e6a40b4f11de90e4f Mon Sep 17 00:00:00 2001 From: Nathan Whitehorn Date: Sat, 1 Feb 2014 17:41:54 +0000 Subject: [PATCH] Provide a simpler and more standards-compliant simplebus implementation to get the Routerboard 800 up and running with the vendor device tree. This does not implement some BERI-specific features (which hopefully won't be necessary soon), so move the old code to mips/beri, with a higher attach priority when built, until MIPS interrupt domain support is rearranged. --- sys/conf/files | 1 - sys/dev/fdt/simplebus.c | 535 ++++++++++++------------- sys/mips/beri/beri_simplebus.c | 425 ++++++++++++++++++++ sys/{dev/fdt => mips/beri}/fdt_ic_if.m | 0 sys/mips/beri/files.beri | 2 + 5 files changed, 688 insertions(+), 275 deletions(-) create mode 100644 sys/mips/beri/beri_simplebus.c rename sys/{dev/fdt => mips/beri}/fdt_ic_if.m (100%) diff --git a/sys/conf/files b/sys/conf/files index 46e669fdae7d..dea592028988 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1367,7 +1367,6 @@ dev/fb/fbd.c optional fbd | vt dev/fb/fb_if.m standard dev/fb/splash.c optional sc splash dev/fdt/fdt_common.c optional fdt -dev/fdt/fdt_ic_if.m optional fdt dev/fdt/fdt_slicer.c optional fdt cfi | fdt nand dev/fdt/fdt_static_dtb.S optional fdt fdt_dtb_static \ dependency "$S/boot/fdt/dts/${FDT_DTS_FILE}" diff --git a/sys/dev/fdt/simplebus.c b/sys/dev/fdt/simplebus.c index b0f8fee203b2..776824f79722 100644 --- a/sys/dev/fdt/simplebus.c +++ b/sys/dev/fdt/simplebus.c @@ -1,10 +1,7 @@ /*- - * Copyright (c) 2009-2010 The FreeBSD Foundation + * Copyright (c) 2013 Nathan Whitehorn * All rights reserved. * - * This software was developed by Semihalf under sponsorship from - * the FreeBSD Foundation. - * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -17,7 +14,7 @@ * 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 + * 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) @@ -29,95 +26,85 @@ #include __FBSDID("$FreeBSD$"); - -#include "opt_platform.h" #include #include -#include -#include #include #include +#include +#include #include -#include +#include #include #include -#include -#include "fdt_common.h" -#include "fdt_ic_if.h" -#include "ofw_bus_if.h" - -#ifdef DEBUG -#define debugf(fmt, args...) do { printf("%s(): ", __func__); \ - printf(fmt,##args); } while (0) -#else -#define debugf(fmt, args...) -#endif - -static MALLOC_DEFINE(M_SIMPLEBUS, "simplebus", "simplebus devices information"); +struct simplebus_range { + uint64_t bus; + uint64_t host; + uint64_t size; +}; struct simplebus_softc { - int sc_addr_cells; - int sc_size_cells; + device_t dev; + phandle_t node; + + struct simplebus_range *ranges; + int nranges; + + pcell_t acells, scells; }; struct simplebus_devinfo { - struct ofw_bus_devinfo di_ofw; - struct resource_list di_res; - - /* Interrupts sense-level info for this device */ - struct fdt_sense_level di_intr_sl[DI_MAX_INTR_NUM]; + struct ofw_bus_devinfo obdinfo; + struct resource_list rl; }; /* - * Prototypes. + * Bus interface. */ -static int simplebus_probe(device_t); -static int simplebus_attach(device_t); - -static int simplebus_print_child(device_t, device_t); -static int simplebus_setup_intr(device_t, device_t, struct resource *, int, - driver_filter_t *, driver_intr_t *, void *, void **); -static int simplebus_teardown_intr(device_t, device_t, struct resource *, - void *); - -static int simplebus_activate_resource(device_t, device_t, int, int, - struct resource *); +static int simplebus_probe(device_t dev); +static int simplebus_attach(device_t dev); static struct resource *simplebus_alloc_resource(device_t, device_t, int, int *, u_long, u_long, u_long, u_int); -static int simplebus_deactivate_resource(device_t, device_t, int, int, - struct resource *); -static int simplebus_release_resource(device_t, device_t, int, int, - struct resource *); -static device_t simplebus_get_interrupt_parent(device_t); -static struct resource_list *simplebus_get_resource_list(device_t, device_t); - -static ofw_bus_get_devinfo_t simplebus_get_devinfo; +static void simplebus_probe_nomatch(device_t bus, device_t child); +static int simplebus_print_child(device_t bus, device_t child); /* - * Bus interface definition. + * ofw_bus interface */ -static device_method_t simplebus_methods[] = { +static const struct ofw_bus_devinfo *simplebus_get_devinfo(device_t bus, + device_t child); + +/* + * local methods + */ + +static int simplebus_fill_ranges(phandle_t node, + struct simplebus_softc *sc); +static struct simplebus_devinfo *simplebus_setup_dinfo(device_t dev, + phandle_t node); + +/* + * Driver methods. + */ +static device_method_t simplebus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, simplebus_probe), DEVMETHOD(device_attach, simplebus_attach), - DEVMETHOD(device_detach, bus_generic_detach), - DEVMETHOD(device_shutdown, bus_generic_shutdown), - DEVMETHOD(device_suspend, bus_generic_suspend), - DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, simplebus_print_child), + DEVMETHOD(bus_probe_nomatch, simplebus_probe_nomatch), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_alloc_resource, simplebus_alloc_resource), - DEVMETHOD(bus_release_resource, simplebus_release_resource), - DEVMETHOD(bus_activate_resource, simplebus_activate_resource), - DEVMETHOD(bus_deactivate_resource, simplebus_deactivate_resource), - DEVMETHOD(bus_setup_intr, simplebus_setup_intr), - DEVMETHOD(bus_teardown_intr, simplebus_teardown_intr), - DEVMETHOD(bus_get_resource_list, simplebus_get_resource_list), + 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_adjust_resource, bus_generic_adjust_resource), + DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str), - /* OFW bus interface */ + /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_devinfo, simplebus_get_devinfo), DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), @@ -125,7 +112,7 @@ static device_method_t simplebus_methods[] = { DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), - { 0, 0 } + DEVMETHOD_END }; static driver_t simplebus_driver = { @@ -133,18 +120,16 @@ static driver_t simplebus_driver = { simplebus_methods, sizeof(struct simplebus_softc) }; - -devclass_t simplebus_devclass; - +static devclass_t simplebus_devclass; DRIVER_MODULE(simplebus, nexus, simplebus_driver, simplebus_devclass, 0, 0); -DRIVER_MODULE(simplebus, simplebus, simplebus_driver, simplebus_devclass, 0, - 0); static int simplebus_probe(device_t dev) { - - if (!ofw_bus_is_compatible(dev, "simple-bus")) + + if (!ofw_bus_is_compatible(dev, "simple-bus") && + (ofw_bus_get_type(dev) == NULL || strcmp(ofw_bus_get_type(dev), + "soc") != 0)) return (ENXIO); device_set_desc(dev, "Flattened device tree simple bus"); @@ -155,102 +140,188 @@ simplebus_probe(device_t dev) static int simplebus_attach(device_t dev) { - device_t dev_child; - struct simplebus_devinfo *di; - struct simplebus_softc *sc; - phandle_t dt_node, dt_child; + struct simplebus_softc *sc; + struct simplebus_devinfo *di; + phandle_t node; + device_t cdev; + node = ofw_bus_get_node(dev); sc = device_get_softc(dev); + sc->dev = dev; + sc->node = node; + /* - * Walk simple-bus and add direct subordinates as our children. + * Some important numbers */ - dt_node = ofw_bus_get_node(dev); - for (dt_child = OF_child(dt_node); dt_child != 0; - dt_child = OF_peer(dt_child)) { + sc->acells = 2; + OF_getencprop(node, "#address-cells", &sc->acells, sizeof(sc->acells)); + sc->scells = 1; + OF_getencprop(node, "#size-cells", &sc->scells, sizeof(sc->scells)); - /* Check and process 'status' property. */ - if (!(fdt_is_enabled(dt_child))) + if (simplebus_fill_ranges(node, sc) < 0) { + device_printf(dev, "could not get ranges\n"); + return (ENXIO); + } + + /* + * In principle, simplebus could have an interrupt map, but ignore that + * for now + */ + + for (node = OF_child(node); node > 0; node = OF_peer(node)) { + if ((di = simplebus_setup_dinfo(dev, node)) == NULL) continue; - - if (!(fdt_pm_is_enabled(dt_child))) - continue; - - di = malloc(sizeof(*di), M_SIMPLEBUS, M_WAITOK | M_ZERO); - - if (ofw_bus_gen_setup_devinfo(&di->di_ofw, dt_child) != 0) { - free(di, M_SIMPLEBUS); - device_printf(dev, "could not set up devinfo\n"); + cdev = device_add_child(dev, NULL, -1); + if (cdev == NULL) { + device_printf(dev, "<%s>: device_add_child failed\n", + di->obdinfo.obd_name); + resource_list_free(&di->rl); + ofw_bus_gen_destroy_devinfo(&di->obdinfo); + free(di, M_DEVBUF); continue; } - - resource_list_init(&di->di_res); - if (fdt_reg_to_rl(dt_child, &di->di_res)) { - device_printf(dev, - "%s: could not process 'reg' " - "property\n", di->di_ofw.obd_name); - ofw_bus_gen_destroy_devinfo(&di->di_ofw); - free(di, M_SIMPLEBUS); - continue; - } - - if (fdt_intr_to_rl(dev, dt_child, &di->di_res, di->di_intr_sl)) { - device_printf(dev, "%s: could not process " - "'interrupts' property\n", di->di_ofw.obd_name); - resource_list_free(&di->di_res); - ofw_bus_gen_destroy_devinfo(&di->di_ofw); - free(di, M_SIMPLEBUS); - continue; - } - - /* Add newbus device for this FDT node */ - dev_child = device_add_child(dev, NULL, -1); - if (dev_child == NULL) { - device_printf(dev, "could not add child: %s\n", - di->di_ofw.obd_name); - resource_list_free(&di->di_res); - ofw_bus_gen_destroy_devinfo(&di->di_ofw); - free(di, M_SIMPLEBUS); - continue; - } -#ifdef DEBUG - device_printf(dev, "added child: %s\n\n", di->di_ofw.obd_name); -#endif - device_set_ivars(dev_child, di); + device_set_ivars(cdev, di); } return (bus_generic_attach(dev)); } static int -simplebus_print_child(device_t dev, device_t child) +simplebus_fill_ranges(phandle_t node, struct simplebus_softc *sc) { - device_t ip; - struct simplebus_devinfo *di; - struct resource_list *rl; - int rv; + int host_address_cells; + cell_t *base_ranges; + ssize_t nbase_ranges; + int err; + int i, j, k; - di = device_get_ivars(child); - rl = &di->di_res; + err = OF_searchencprop(OF_parent(node), "#address-cells", + &host_address_cells, sizeof(host_address_cells)); + if (err <= 0) + return (-1); - rv = 0; - rv += bus_print_child_header(dev, child); - rv += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); - rv += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); - if ((ip = simplebus_get_interrupt_parent(child)) != NULL) - rv += printf(" (%s)", device_get_nameunit(ip)); - rv += bus_print_child_footer(dev, child); + nbase_ranges = OF_getproplen(node, "ranges"); + if (nbase_ranges < 0) + return (-1); + sc->nranges = nbase_ranges / sizeof(cell_t) / + (sc->acells + host_address_cells + sc->scells); + if (sc->nranges == 0) + return (0); - return (rv); + sc->ranges = malloc(sc->nranges * sizeof(sc->ranges[0]), + M_DEVBUF, M_WAITOK); + base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK); + OF_getencprop(node, "ranges", base_ranges, nbase_ranges); + + for (i = 0, j = 0; i < sc->nranges; i++) { + sc->ranges[i].bus = 0; + for (k = 0; k < sc->acells; k++) { + sc->ranges[i].bus <<= 32; + sc->ranges[i].bus |= base_ranges[j++]; + } + sc->ranges[i].host = 0; + for (k = 0; k < host_address_cells; k++) { + sc->ranges[i].host <<= 32; + sc->ranges[i].host |= base_ranges[j++]; + } + sc->ranges[i].size = 0; + for (k = 0; k < sc->scells; k++) { + sc->ranges[i].size <<= 32; + sc->ranges[i].size |= base_ranges[j++]; + } + } + + free(base_ranges, M_DEVBUF); + return (sc->nranges); +} + +static struct simplebus_devinfo * +simplebus_setup_dinfo(device_t dev, phandle_t node) +{ + struct simplebus_softc *sc; + struct simplebus_devinfo *ndi; + uint32_t *reg, *intr, icells; + uint64_t phys, size; + phandle_t iparent; + int i, j, k; + int nintr; + int nreg; + + sc = device_get_softc(dev); + + ndi = malloc(sizeof(*ndi), M_DEVBUF, M_WAITOK | M_ZERO); + if (ofw_bus_gen_setup_devinfo(&ndi->obdinfo, node) != 0) { + free(ndi, M_DEVBUF); + return (NULL); + } + + resource_list_init(&ndi->rl); + nreg = OF_getencprop_alloc(node, "reg", sizeof(*reg), (void **)®); + if (nreg == -1) + nreg = 0; + if (nreg % (sc->acells + sc->scells) != 0) { + if (bootverbose) + device_printf(dev, "Malformed reg property on <%s>\n", + ndi->obdinfo.obd_name); + nreg = 0; + } + + for (i = 0, k = 0; i < nreg; i += sc->acells + sc->scells, k++) { + phys = size = 0; + for (j = 0; j < sc->acells; j++) { + phys <<= 32; + phys |= reg[i + j]; + } + for (j = 0; j < sc->scells; j++) { + size <<= 32; + size |= reg[i + sc->acells + j]; + } + + resource_list_add(&ndi->rl, SYS_RES_MEMORY, k, + phys, phys + size - 1, size); + } + free(reg, M_OFWPROP); + + nintr = OF_getencprop_alloc(node, "interrupts", sizeof(*intr), + (void **)&intr); + if (nintr > 0) { + iparent = 0; + OF_searchencprop(node, "interrupt-parent", &iparent, + sizeof(iparent)); + OF_searchencprop(OF_xref_phandle(iparent), "#interrupt-cells", + &icells, sizeof(icells)); + for (i = 0, k = 0; i < nintr; i += icells, k++) { + intr[i] = ofw_bus_map_intr(dev, iparent, icells, + &intr[i]); + resource_list_add(&ndi->rl, SYS_RES_IRQ, k, intr[i], + intr[i], 1); + } + free(intr, M_OFWPROP); + } + + return (ndi); +} + +static const struct ofw_bus_devinfo * +simplebus_get_devinfo(device_t bus __unused, device_t child) +{ + struct simplebus_devinfo *ndi; + + ndi = device_get_ivars(child); + return (&ndi->obdinfo); } static struct resource * simplebus_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { - device_t ic; + struct simplebus_softc *sc; struct simplebus_devinfo *di; struct resource_list_entry *rle; + int j; + + sc = device_get_softc(bus); /* * Request for the default allocation with a given rid: use resource @@ -263,7 +334,7 @@ simplebus_alloc_resource(device_t bus, device_t child, int type, int *rid, if (type == SYS_RES_IOPORT) type = SYS_RES_MEMORY; - rle = resource_list_find(&di->di_res, type, *rid); + rle = resource_list_find(&di->rl, type, *rid); if (rle == NULL) { if (bootverbose) device_printf(bus, "no default resources for " @@ -273,153 +344,69 @@ simplebus_alloc_resource(device_t bus, device_t child, int type, int *rid, start = rle->start; end = rle->end; count = rle->count; - } + } - if (type == SYS_RES_IRQ && - (ic = simplebus_get_interrupt_parent(child)) != NULL) - return(FDT_IC_ALLOC_INTR(ic, child, rid, start, flags)); + if (type == SYS_RES_MEMORY) { + /* Remap through ranges property */ + for (j = 0; j < sc->nranges; j++) { + if (start >= sc->ranges[j].bus && end < + sc->ranges[j].bus + sc->ranges[j].size) { + start -= sc->ranges[j].bus; + start += sc->ranges[j].host; + end -= sc->ranges[j].bus; + end += sc->ranges[j].host; + break; + } + } + if (j == sc->nranges && sc->nranges != 0) { + if (bootverbose) + device_printf(bus, "Could not map resource " + "%#lx-%#lx\n", start, end); + + return (NULL); + } + } return (bus_generic_alloc_resource(bus, child, type, rid, start, end, count, flags)); } static int -simplebus_activate_resource(device_t dev, device_t child, int type, int rid, - struct resource *r) +simplebus_print_res(struct simplebus_devinfo *di) { - device_t ic; + int rv; - if (type == SYS_RES_IRQ && - (ic = simplebus_get_interrupt_parent(child)) != NULL) - return (FDT_IC_ACTIVATE_INTR(ic, r)); + rv = 0; + rv += resource_list_print_type(&di->rl, "mem", SYS_RES_MEMORY, "%#lx"); + rv += resource_list_print_type(&di->rl, "irq", SYS_RES_IRQ, "%ld"); + return (rv); +} - return (bus_generic_activate_resource(dev, child, type, rid, r)); +static void +simplebus_probe_nomatch(device_t bus, device_t child) +{ + const char *name, *type; + + if (!bootverbose) + return; + + name = ofw_bus_get_name(child); + type = ofw_bus_get_type(child); + + device_printf(bus, "<%s>", name != NULL ? name : "unknown"); + simplebus_print_res(device_get_ivars(child)); + printf(" type %s (no driver attached)\n", + type != NULL ? type : "unknown"); } static int -simplebus_deactivate_resource(device_t dev, device_t child, int type, int rid, - struct resource *r) +simplebus_print_child(device_t bus, device_t child) { - device_t ic; + int rv; - if (type == SYS_RES_IRQ && - (ic = simplebus_get_interrupt_parent(child)) != NULL) - return (FDT_IC_DEACTIVATE_INTR(ic, r)); - - return (bus_generic_deactivate_resource(dev, child, type, rid, r)); + rv = bus_print_child_header(bus, child); + rv += simplebus_print_res(device_get_ivars(child)); + rv += bus_print_child_footer(bus, child); + return (rv); } -static int -simplebus_release_resource(device_t dev, device_t child, int type, int rid, - struct resource *r) -{ - device_t ic; - - if (type == SYS_RES_IRQ && - (ic = simplebus_get_interrupt_parent(child)) != NULL) - return (FDT_IC_RELEASE_INTR(ic, r)); - - return (bus_generic_release_resource(dev, child, type, rid, r)); -} - -static struct resource_list * -simplebus_get_resource_list(device_t bus, device_t child) -{ - struct simplebus_devinfo *di; - - di = device_get_ivars(child); - return (&di->di_res); -} - -static device_t -simplebus_get_interrupt_parent(device_t dev) -{ - struct simplebus_devinfo *di; - struct fdt_ic *ic; - device_t ip; - phandle_t ph, iph; - - ip = NULL; - - di = device_get_ivars(dev); - if (di == NULL) - return (NULL); - - if (OF_getencprop(di->di_ofw.obd_node, "interrupt-parent", &iph, - sizeof(iph)) > 0) { - ph = OF_xref_phandle(iph); - SLIST_FOREACH(ic, &fdt_ic_list_head, fdt_ics) { - if (ic->iph == ph) { - ip = ic->dev; - break; - } - } - } - return (ip); -} - -static int -simplebus_setup_intr(device_t bus, device_t child, struct resource *res, - int flags, driver_filter_t *filter, driver_intr_t *ihand, void *arg, - void **cookiep) -{ - struct simplebus_devinfo *di; - device_t ic; - enum intr_trigger trig; - enum intr_polarity pol; - int error, irq, rid; - - di = device_get_ivars(child); - if (di == NULL) - return (ENXIO); - - if (res == NULL) - return (EINVAL); - - rid = rman_get_rid(res); - if (rid >= DI_MAX_INTR_NUM) - return (ENOENT); - - ic = simplebus_get_interrupt_parent(child); - - trig = di->di_intr_sl[rid].trig; - pol = di->di_intr_sl[rid].pol; - if (trig != INTR_TRIGGER_CONFORM || pol != INTR_POLARITY_CONFORM) { - irq = rman_get_start(res); - if (ic != NULL) - error = FDT_IC_CONFIG_INTR(ic, irq, trig, pol); - else - error = bus_generic_config_intr(bus, irq, trig, pol); - if (error) - return (error); - } - - if (ic != NULL) - error = FDT_IC_SETUP_INTR(ic, child, res, flags, filter, - ihand, arg, cookiep); - else - error = bus_generic_setup_intr(bus, child, res, flags, filter, - ihand, arg, cookiep); - return (error); -} - -static int -simplebus_teardown_intr(device_t bus, device_t child, struct resource *res, - void *cookie) -{ - device_t ic; - - if ((ic = simplebus_get_interrupt_parent(child)) != NULL) - return (FDT_IC_TEARDOWN_INTR(ic, child, res, cookie)); - - return (bus_generic_teardown_intr(bus, child, res, cookie)); -} - -static const struct ofw_bus_devinfo * -simplebus_get_devinfo(device_t bus, device_t child) -{ - struct simplebus_devinfo *di; - - di = device_get_ivars(child); - return (&di->di_ofw); -} diff --git a/sys/mips/beri/beri_simplebus.c b/sys/mips/beri/beri_simplebus.c new file mode 100644 index 000000000000..58927f9417f2 --- /dev/null +++ b/sys/mips/beri/beri_simplebus.c @@ -0,0 +1,425 @@ +/*- + * Copyright (c) 2009-2010 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Semihalf under sponsorship from + * the FreeBSD Foundation. + * + * 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 +__FBSDID("$FreeBSD$"); + +#include "opt_platform.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include "fdt_ic_if.h" +#include "ofw_bus_if.h" + +#ifdef DEBUG +#define debugf(fmt, args...) do { printf("%s(): ", __func__); \ + printf(fmt,##args); } while (0) +#else +#define debugf(fmt, args...) +#endif + +static MALLOC_DEFINE(M_SIMPLEBUS, "simplebus", "simplebus devices information"); + +struct simplebus_softc { + int sc_addr_cells; + int sc_size_cells; +}; + +struct simplebus_devinfo { + struct ofw_bus_devinfo di_ofw; + struct resource_list di_res; + + /* Interrupts sense-level info for this device */ + struct fdt_sense_level di_intr_sl[DI_MAX_INTR_NUM]; +}; + +/* + * Prototypes. + */ +static int simplebus_probe(device_t); +static int simplebus_attach(device_t); + +static int simplebus_print_child(device_t, device_t); +static int simplebus_setup_intr(device_t, device_t, struct resource *, int, + driver_filter_t *, driver_intr_t *, void *, void **); +static int simplebus_teardown_intr(device_t, device_t, struct resource *, + void *); + +static int simplebus_activate_resource(device_t, device_t, int, int, + struct resource *); +static struct resource *simplebus_alloc_resource(device_t, device_t, int, + int *, u_long, u_long, u_long, u_int); +static int simplebus_deactivate_resource(device_t, device_t, int, int, + struct resource *); +static int simplebus_release_resource(device_t, device_t, int, int, + struct resource *); +static device_t simplebus_get_interrupt_parent(device_t); +static struct resource_list *simplebus_get_resource_list(device_t, device_t); + +static ofw_bus_get_devinfo_t simplebus_get_devinfo; + +/* + * Bus interface definition. + */ +static device_method_t simplebus_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, simplebus_probe), + DEVMETHOD(device_attach, simplebus_attach), + DEVMETHOD(device_detach, bus_generic_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* Bus interface */ + DEVMETHOD(bus_print_child, simplebus_print_child), + DEVMETHOD(bus_alloc_resource, simplebus_alloc_resource), + DEVMETHOD(bus_release_resource, simplebus_release_resource), + DEVMETHOD(bus_activate_resource, simplebus_activate_resource), + DEVMETHOD(bus_deactivate_resource, simplebus_deactivate_resource), + DEVMETHOD(bus_setup_intr, simplebus_setup_intr), + DEVMETHOD(bus_teardown_intr, simplebus_teardown_intr), + DEVMETHOD(bus_get_resource_list, simplebus_get_resource_list), + + /* OFW bus interface */ + DEVMETHOD(ofw_bus_get_devinfo, simplebus_get_devinfo), + DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), + DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), + DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), + DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), + DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), + + { 0, 0 } +}; + +static driver_t simplebus_driver = { + "simplebus", + simplebus_methods, + sizeof(struct simplebus_softc) +}; + +devclass_t simplebus_devclass; + +DRIVER_MODULE(simplebus, nexus, simplebus_driver, simplebus_devclass, 0, 0); +DRIVER_MODULE(simplebus, simplebus, simplebus_driver, simplebus_devclass, 0, + 0); + +static int +simplebus_probe(device_t dev) +{ + + if (!ofw_bus_is_compatible(dev, "simple-bus")) + return (ENXIO); + + device_set_desc(dev, "Flattened device tree simple bus"); + + return (BUS_PROBE_SPECIFIC); +} + +static int +simplebus_attach(device_t dev) +{ + device_t dev_child; + struct simplebus_devinfo *di; + struct simplebus_softc *sc; + phandle_t dt_node, dt_child; + + sc = device_get_softc(dev); + + /* + * Walk simple-bus and add direct subordinates as our children. + */ + dt_node = ofw_bus_get_node(dev); + for (dt_child = OF_child(dt_node); dt_child != 0; + dt_child = OF_peer(dt_child)) { + + /* Check and process 'status' property. */ + if (!(fdt_is_enabled(dt_child))) + continue; + + if (!(fdt_pm_is_enabled(dt_child))) + continue; + + di = malloc(sizeof(*di), M_SIMPLEBUS, M_WAITOK | M_ZERO); + + if (ofw_bus_gen_setup_devinfo(&di->di_ofw, dt_child) != 0) { + free(di, M_SIMPLEBUS); + device_printf(dev, "could not set up devinfo\n"); + continue; + } + + resource_list_init(&di->di_res); + if (fdt_reg_to_rl(dt_child, &di->di_res)) { + device_printf(dev, + "%s: could not process 'reg' " + "property\n", di->di_ofw.obd_name); + ofw_bus_gen_destroy_devinfo(&di->di_ofw); + free(di, M_SIMPLEBUS); + continue; + } + + if (fdt_intr_to_rl(dev, dt_child, &di->di_res, di->di_intr_sl)) { + device_printf(dev, "%s: could not process " + "'interrupts' property\n", di->di_ofw.obd_name); + resource_list_free(&di->di_res); + ofw_bus_gen_destroy_devinfo(&di->di_ofw); + free(di, M_SIMPLEBUS); + continue; + } + + /* Add newbus device for this FDT node */ + dev_child = device_add_child(dev, NULL, -1); + if (dev_child == NULL) { + device_printf(dev, "could not add child: %s\n", + di->di_ofw.obd_name); + resource_list_free(&di->di_res); + ofw_bus_gen_destroy_devinfo(&di->di_ofw); + free(di, M_SIMPLEBUS); + continue; + } +#ifdef DEBUG + device_printf(dev, "added child: %s\n\n", di->di_ofw.obd_name); +#endif + device_set_ivars(dev_child, di); + } + + return (bus_generic_attach(dev)); +} + +static int +simplebus_print_child(device_t dev, device_t child) +{ + device_t ip; + struct simplebus_devinfo *di; + struct resource_list *rl; + int rv; + + di = device_get_ivars(child); + rl = &di->di_res; + + rv = 0; + rv += bus_print_child_header(dev, child); + rv += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); + rv += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); + if ((ip = simplebus_get_interrupt_parent(child)) != NULL) + rv += printf(" (%s)", device_get_nameunit(ip)); + rv += bus_print_child_footer(dev, child); + + return (rv); +} + +static struct resource * +simplebus_alloc_resource(device_t bus, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + device_t ic; + struct simplebus_devinfo *di; + struct resource_list_entry *rle; + + /* + * Request for the default allocation with a given rid: use resource + * list stored in the local device info. + */ + if ((start == 0UL) && (end == ~0UL)) { + if ((di = device_get_ivars(child)) == NULL) + return (NULL); + + if (type == SYS_RES_IOPORT) + type = SYS_RES_MEMORY; + + rle = resource_list_find(&di->di_res, type, *rid); + if (rle == NULL) { + if (bootverbose) + device_printf(bus, "no default resources for " + "rid = %d, type = %d\n", *rid, type); + return (NULL); + } + start = rle->start; + end = rle->end; + count = rle->count; + } + + if (type == SYS_RES_IRQ && + (ic = simplebus_get_interrupt_parent(child)) != NULL) + return(FDT_IC_ALLOC_INTR(ic, child, rid, start, flags)); + + return (bus_generic_alloc_resource(bus, child, type, rid, start, end, + count, flags)); +} + +static int +simplebus_activate_resource(device_t dev, device_t child, int type, int rid, + struct resource *r) +{ + device_t ic; + + if (type == SYS_RES_IRQ && + (ic = simplebus_get_interrupt_parent(child)) != NULL) + return (FDT_IC_ACTIVATE_INTR(ic, r)); + + return (bus_generic_activate_resource(dev, child, type, rid, r)); +} + +static int +simplebus_deactivate_resource(device_t dev, device_t child, int type, int rid, + struct resource *r) +{ + device_t ic; + + if (type == SYS_RES_IRQ && + (ic = simplebus_get_interrupt_parent(child)) != NULL) + return (FDT_IC_DEACTIVATE_INTR(ic, r)); + + return (bus_generic_deactivate_resource(dev, child, type, rid, r)); +} + +static int +simplebus_release_resource(device_t dev, device_t child, int type, int rid, + struct resource *r) +{ + device_t ic; + + if (type == SYS_RES_IRQ && + (ic = simplebus_get_interrupt_parent(child)) != NULL) + return (FDT_IC_RELEASE_INTR(ic, r)); + + return (bus_generic_release_resource(dev, child, type, rid, r)); +} + +static struct resource_list * +simplebus_get_resource_list(device_t bus, device_t child) +{ + struct simplebus_devinfo *di; + + di = device_get_ivars(child); + return (&di->di_res); +} + +static device_t +simplebus_get_interrupt_parent(device_t dev) +{ + struct simplebus_devinfo *di; + struct fdt_ic *ic; + device_t ip; + phandle_t ph, iph; + + ip = NULL; + + di = device_get_ivars(dev); + if (di == NULL) + return (NULL); + + if (OF_getencprop(di->di_ofw.obd_node, "interrupt-parent", &iph, + sizeof(iph)) > 0) { + ph = OF_xref_phandle(iph); + SLIST_FOREACH(ic, &fdt_ic_list_head, fdt_ics) { + if (ic->iph == ph) { + ip = ic->dev; + break; + } + } + } + return (ip); +} + +static int +simplebus_setup_intr(device_t bus, device_t child, struct resource *res, + int flags, driver_filter_t *filter, driver_intr_t *ihand, void *arg, + void **cookiep) +{ + struct simplebus_devinfo *di; + device_t ic; + enum intr_trigger trig; + enum intr_polarity pol; + int error, irq, rid; + + di = device_get_ivars(child); + if (di == NULL) + return (ENXIO); + + if (res == NULL) + return (EINVAL); + + rid = rman_get_rid(res); + if (rid >= DI_MAX_INTR_NUM) + return (ENOENT); + + ic = simplebus_get_interrupt_parent(child); + + trig = di->di_intr_sl[rid].trig; + pol = di->di_intr_sl[rid].pol; + if (trig != INTR_TRIGGER_CONFORM || pol != INTR_POLARITY_CONFORM) { + irq = rman_get_start(res); + if (ic != NULL) + error = FDT_IC_CONFIG_INTR(ic, irq, trig, pol); + else + error = bus_generic_config_intr(bus, irq, trig, pol); + if (error) + return (error); + } + + if (ic != NULL) + error = FDT_IC_SETUP_INTR(ic, child, res, flags, filter, + ihand, arg, cookiep); + else + error = bus_generic_setup_intr(bus, child, res, flags, filter, + ihand, arg, cookiep); + return (error); +} + +static int +simplebus_teardown_intr(device_t bus, device_t child, struct resource *res, + void *cookie) +{ + device_t ic; + + if ((ic = simplebus_get_interrupt_parent(child)) != NULL) + return (FDT_IC_TEARDOWN_INTR(ic, child, res, cookie)); + + return (bus_generic_teardown_intr(bus, child, res, cookie)); +} + +static const struct ofw_bus_devinfo * +simplebus_get_devinfo(device_t bus, device_t child) +{ + struct simplebus_devinfo *di; + + di = device_get_ivars(child); + return (&di->di_ofw); +} diff --git a/sys/dev/fdt/fdt_ic_if.m b/sys/mips/beri/fdt_ic_if.m similarity index 100% rename from sys/dev/fdt/fdt_ic_if.m rename to sys/mips/beri/fdt_ic_if.m diff --git a/sys/mips/beri/files.beri b/sys/mips/beri/files.beri index 7bdcb3176232..de7ccc3e3c24 100644 --- a/sys/mips/beri/files.beri +++ b/sys/mips/beri/files.beri @@ -18,5 +18,7 @@ dev/terasic/mtl/terasic_mtl_syscons.c optional terasic_mtl dev/terasic/mtl/terasic_mtl_text.c optional terasic_mtl mips/beri/beri_machdep.c standard mips/beri/beri_pic.c optional fdt +mips/beri/beri_simplebus.c optional fdt +mips/beri/fdt_ic_if.m optional fdt mips/mips/intr_machdep.c standard mips/mips/tick.c standard