From 27f0a13f0adbfdb658bb63aac86772e0107c1c20 Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 15 May 2014 14:26:11 +0000 Subject: MFC r261351, r261352, r261355, r261396, r261397, r261398, r261403, r261404, r261405 Open Firmware interrupt specifiers can consist of arbitrary-length byte strings and include arbitrary information (IRQ line/domain/sense). When the ofw_bus_map_intr() API was introduced, it assumed that, as on most systems, these were either 1 cell, containing an interrupt line, or 2, containing a line number plus a sense code. It turns out a non-negligible number of ARM systems use 3 (or even 4!) cells for interrupts, so make this more general. 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. Allow nesting of simplebuses. Add a set of helpers (ofw_bus_get_status() and ofw_bus_status_okay()) to process "status" properties of OF nodes. Fix one remnant endian flaw in nexus. --- sys/mips/beri/beri_simplebus.c | 425 +++++++++++++++++++++++++++++++++++++++++ sys/mips/beri/fdt_ic_if.m | 266 ++++++++++++++++++++++++++ sys/mips/beri/files.beri | 2 + 3 files changed, 693 insertions(+) create mode 100644 sys/mips/beri/beri_simplebus.c create mode 100644 sys/mips/beri/fdt_ic_if.m (limited to 'sys/mips') diff --git a/sys/mips/beri/beri_simplebus.c b/sys/mips/beri/beri_simplebus.c new file mode 100644 index 0000000..58927f9 --- /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/mips/beri/fdt_ic_if.m b/sys/mips/beri/fdt_ic_if.m new file mode 100644 index 0000000..e11b8f8 --- /dev/null +++ b/sys/mips/beri/fdt_ic_if.m @@ -0,0 +1,266 @@ +#- +# Copyright (c) 2013 SRI International +# Copyright (c) 1998-2004 Doug Rabson +# 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 +#include +#include + +/** + * @defgroup FST_IC fdt_ic - KObj methods for interrupt controllers + * @brief A set of methods required device drivers that are interrupt + * controllers. Derived from sys/kern/bus_if.m. + * @{ + */ +INTERFACE fdt_ic; + +/** + * @brief Allocate an interrupt resource + * + * This method is called by child devices of an interrupt controller to + * allocate an interrup. The meaning of the resource-ID field varies + * from bus to bus and is opaque to the interrupt controller. If a + * resource was allocated and the caller did not use the RF_ACTIVE + * to specify that it should be activated immediately, the caller is + * responsible for calling FDT_IC_ACTIVATE_INTR() when it actually uses + * the interupt. + * + * @param _dev the interrupt-parent device of @p _child + * @param _child the device which is requesting an allocation + * @param _rid a pointer to the resource identifier + * @param _irq interrupt source to allocate + * @param _flags any extra flags to control the resource + * allocation - see @c RF_XXX flags in + * for details + * + * @returns the interrupt which was allocated or @c NULL if no + * resource could be allocated + */ +METHOD struct resource * alloc_intr { + device_t _dev; + device_t _child; + int *_rid; + u_long _irq; + u_int _flags; +}; + +/** + * @brief Activate an interrupt + * + * Activate an interrupt previously allocated with FDT_IC_ALLOC_INTR(). + * + * @param _dev the parent device of @p _child + * @param _r interrupt to activate + */ +METHOD int activate_intr { + device_t _dev; + struct resource *_r; +}; + +/** + * @brief Deactivate an interrupt + * + * Deactivate a resource previously allocated with FDT_IC_ALLOC_INTR(). + * + * @param _dev the parent device of @p _child + * @param _r the interrupt to deactivate + */ +METHOD int deactivate_intr { + device_t _dev; + struct resource *_r; +}; + +/** + * @brief Release an interrupt + * + * Free an interupt allocated by the FDT_IC_ALLOC_INTR. + * + * @param _dev the parent device of @p _child + * @param _r the resource to release + */ +METHOD int release_intr { + device_t _dev; + struct resource *_res; +}; + +/** + * @brief Install an interrupt handler + * + * This method is used to associate an interrupt handler function with + * an irq resource. When the interrupt triggers, the function @p _intr + * will be called with the value of @p _arg as its single + * argument. The value returned in @p *_cookiep is used to cancel the + * interrupt handler - the caller should save this value to use in a + * future call to FDT_IC_TEARDOWN_INTR(). + * + * @param _dev the interrupt-parent device of @p _child + * @param _child the device which allocated the resource + * @param _irq the resource representing the interrupt + * @param _flags a set of bits from enum intr_type specifying + * the class of interrupt + * @param _intr the function to call when the interrupt + * triggers + * @param _arg a value to use as the single argument in calls + * to @p _intr + * @param _cookiep a pointer to a location to recieve a cookie + * value that may be used to remove the interrupt + * handler + */ +METHOD int setup_intr { + device_t _dev; + device_t _child; + struct resource *_irq; + int _flags; + driver_filter_t *_filter; + driver_intr_t *_intr; + void *_arg; + void **_cookiep; +}; + +/** + * @brief Uninstall an interrupt handler + * + * This method is used to disassociate an interrupt handler function + * with an irq resource. The value of @p _cookie must be the value + * returned from a previous call to FDT_IC_SETUP_INTR(). + * + * @param _dev the interrupt-parent device of @p _child + * @param _child the device which allocated the resource + * @param _irq the resource representing the interrupt + * @param _cookie the cookie value returned when the interrupt + * was originally registered + */ +METHOD int teardown_intr { + device_t _dev; + device_t _child; + struct resource *_irq; + void *_cookie; +}; + +/** + * @brief Allow drivers to request that an interrupt be bound to a specific + * CPU. + * + * @param _dev the interrupt-parent device of @p _child + * @param _child the device which allocated the resource + * @param _irq the resource representing the interrupt + * @param _cpu the CPU to bind the interrupt to + */ +METHOD int bind_intr { + device_t _dev; + device_t _child; + struct resource *_irq; + int _cpu; +}; + +/** + * @brief Allow drivers to specify the trigger mode and polarity + * of the specified interrupt. + * + * @param _dev the interrupt-parent device + * @param _irq the interrupt number to modify + * @param _trig the trigger mode required + * @param _pol the interrupt polarity required + */ +METHOD int config_intr { + device_t _dev; + int _irq; + enum intr_trigger _trig; + enum intr_polarity _pol; +}; + +/** + * @brief Allow drivers to associate a description with an active + * interrupt handler. + * + * @param _dev the interrupt-parent device of @p _child + * @param _child the device which allocated the resource + * @param _irq the resource representing the interrupt + * @param _cookie the cookie value returned when the interrupt + * was originally registered + * @param _descr the description to associate with the interrupt + */ +METHOD int describe_intr { + device_t _dev; + device_t _child; + struct resource *_irq; + void *_cookie; + const char *_descr; +}; + +/** + * @brief Notify an ic that specified child's IRQ should be remapped. + * + * @param _dev the interrupt-parent device + * @param _child the child device + * @param _irq the irq number + */ +METHOD int remap_intr { + device_t _dev; + device_t _child; + u_int _irq; +}; + +/** + * @brief Enable an IPI source. + * + * @param _dev the interrupt controller + * @param _tid the thread ID (relative to the interrupt controller) + * to enable IPIs for + * @param _ipi_irq hardware IRQ to send IPIs to + */ +METHOD void setup_ipi { + device_t _dev; + u_int _tid; + u_int _irq; +}; + +/** + * @brief Send an IPI to the specified thread. + * + * @param _dev the interrupt controller + * @param _tid the thread ID (relative to the interrupt controller) + * to send IPIs to + */ +METHOD void send_ipi { + device_t _dev; + u_int _tid; +}; + +/** + * @brief Clear the IPI on the specfied thread. Only call with the + * local hardware thread or interrupts may be lost! + * + * @param _dev the interrupt controller + * @param _tid the thread ID (relative to the interrupt controller) + * to clear the IPI on + */ +METHOD void clear_ipi { + device_t _dev; + u_int _tid; +}; diff --git a/sys/mips/beri/files.beri b/sys/mips/beri/files.beri index 7bdcb31..de7ccc3 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 -- cgit v1.1