diff options
author | brooks <brooks@FreeBSD.org> | 2013-10-31 16:18:36 +0000 |
---|---|---|
committer | brooks <brooks@FreeBSD.org> | 2013-10-31 16:18:36 +0000 |
commit | 7e2e9ae1af9f1eeef71d67a4beb036f54fdf9406 (patch) | |
tree | e1b6887d8f8516aa9dd6ddcdc894598368c5d9f0 /sys/dev/fdt | |
parent | 8b58367431f0c0ea5889dee10a939c56c516b590 (diff) | |
download | FreeBSD-src-7e2e9ae1af9f1eeef71d67a4beb036f54fdf9406.zip FreeBSD-src-7e2e9ae1af9f1eeef71d67a4beb036f54fdf9406.tar.gz |
MFC r256861:
MFP4: 223121 (FDT infrastructure portion)
Implement support for interrupt-parent nodes in simplebus. The current
implementation requires that device declarations have an interrupt-parent
node and that it point to a device that has registered itself as a
interrupt controller in fdt_ic_list_head and implements the fdt_ic
interface.
Sponsored by: DARPA/AFRL
Approved by: re (gjb)
Diffstat (limited to 'sys/dev/fdt')
-rw-r--r-- | sys/dev/fdt/fdt_common.c | 2 | ||||
-rw-r--r-- | sys/dev/fdt/fdt_common.h | 7 | ||||
-rw-r--r-- | sys/dev/fdt/fdt_ic_if.m | 266 | ||||
-rw-r--r-- | sys/dev/fdt/simplebus.c | 126 |
4 files changed, 392 insertions, 9 deletions
diff --git a/sys/dev/fdt/fdt_common.c b/sys/dev/fdt/fdt_common.c index e8cd11f..dad4297 100644 --- a/sys/dev/fdt/fdt_common.c +++ b/sys/dev/fdt/fdt_common.c @@ -63,6 +63,8 @@ vm_paddr_t fdt_immr_pa; vm_offset_t fdt_immr_va; vm_offset_t fdt_immr_size; +struct fdt_ic_list fdt_ic_list_head = SLIST_HEAD_INITIALIZER(fdt_ic_list_head); + int fdt_get_range(phandle_t node, int range_id, u_long *base, u_long *size) { diff --git a/sys/dev/fdt/fdt_common.h b/sys/dev/fdt/fdt_common.h index d244a17..8c16257 100644 --- a/sys/dev/fdt/fdt_common.h +++ b/sys/dev/fdt/fdt_common.h @@ -70,6 +70,13 @@ struct fdt_fixup_entry { }; extern struct fdt_fixup_entry fdt_fixup_table[]; +extern SLIST_HEAD(fdt_ic_list, fdt_ic) fdt_ic_list_head; +struct fdt_ic { + SLIST_ENTRY(fdt_ic) fdt_ics; + ihandle_t iph; + device_t dev; +}; + extern vm_paddr_t fdt_immr_pa; extern vm_offset_t fdt_immr_va; extern vm_offset_t fdt_immr_size; diff --git a/sys/dev/fdt/fdt_ic_if.m b/sys/dev/fdt/fdt_ic_if.m new file mode 100644 index 0000000..e11b8f8 --- /dev/null +++ b/sys/dev/fdt/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 <sys/types.h> +#include <sys/systm.h> +#include <sys/bus.h> + +/** + * @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 + * <sys/rman.h> 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/dev/fdt/simplebus.c b/sys/dev/fdt/simplebus.c index b4e7bfb..6efe390 100644 --- a/sys/dev/fdt/simplebus.c +++ b/sys/dev/fdt/simplebus.c @@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$"); #include <dev/ofw/openfirm.h> #include "fdt_common.h" +#include "fdt_ic_if.h" #include "ofw_bus_if.h" #ifdef DEBUG @@ -80,9 +81,18 @@ 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; @@ -102,11 +112,11 @@ static device_method_t simplebus_methods[] = { /* Bus interface */ DEVMETHOD(bus_print_child, simplebus_print_child), DEVMETHOD(bus_alloc_resource, simplebus_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_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, bus_generic_teardown_intr), + DEVMETHOD(bus_teardown_intr, simplebus_teardown_intr), DEVMETHOD(bus_get_resource_list, simplebus_get_resource_list), /* OFW bus interface */ @@ -217,6 +227,7 @@ simplebus_attach(device_t 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; @@ -228,6 +239,8 @@ simplebus_print_child(device_t dev, device_t child) 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); @@ -237,6 +250,7 @@ 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; @@ -263,10 +277,53 @@ simplebus_alloc_resource(device_t bus, device_t child, int type, int *rid, 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) { @@ -276,15 +333,45 @@ simplebus_get_resource_list(device_t bus, device_t 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; + ihandle_t iph; + phandle_t ph; + + ip = NULL; + + di = device_get_ivars(dev); + if (di == NULL) + return (NULL); + + if (OF_getprop(di->di_ofw.obd_node, "interrupt-parent", &iph, + sizeof(iph)) > 0) { + iph = fdt32_to_cpu(iph); + ph = OF_instance_to_package(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, rid; + int error, irq, rid; di = device_get_ivars(child); if (di == NULL) @@ -297,20 +384,41 @@ simplebus_setup_intr(device_t bus, device_t child, struct resource *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) { - error = bus_generic_config_intr(bus, rman_get_start(res), - trig, pol); + 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); } - error = bus_generic_setup_intr(bus, child, res, flags, filter, ihand, - arg, cookiep); + 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) { |