The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/mips/sibyte/sb_zbbus.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2009 Neelkanth Natu
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   26  * SUCH DAMAGE.
   27  */
   28 
   29 #include <sys/cdefs.h>
   30 __FBSDID("$FreeBSD: releng/12.0/sys/mips/sibyte/sb_zbbus.c 326259 2017-11-27 15:07:26Z pfg $");
   31 
   32 #include <sys/param.h>
   33 #include <sys/kernel.h>
   34 #include <sys/systm.h>
   35 #include <sys/module.h>
   36 #include <sys/bus.h>
   37 #include <sys/malloc.h>
   38 #include <sys/rman.h>
   39 #include <sys/lock.h>
   40 #include <sys/mutex.h>
   41 
   42 #include <machine/resource.h>
   43 #include <machine/intr_machdep.h>
   44 
   45 #include "sb_scd.h"
   46 
   47 static MALLOC_DEFINE(M_INTMAP, "sb1250 intmap", "Sibyte 1250 Interrupt Mapper");
   48 
   49 static struct mtx zbbus_intr_mtx;
   50 MTX_SYSINIT(zbbus_intr_mtx, &zbbus_intr_mtx, "zbbus_intr_mask/unmask lock",
   51             MTX_SPIN);
   52 
   53 /*
   54  * This array holds the mapping between a MIPS hard interrupt and the
   55  * interrupt sources that feed into that it.
   56  */
   57 static uint64_t hardint_to_intsrc_mask[NHARD_IRQS];
   58 
   59 struct sb_intmap {
   60         int intsrc;             /* interrupt mapper register number (0 - 63) */
   61         int hardint;            /* cpu interrupt from 0 to NHARD_IRQS - 1 */
   62 
   63         /*
   64          * The device that the interrupt belongs to. Note that multiple
   65          * devices may share an interrupt. For e.g. PCI_INT_x lines.
   66          *
   67          * The device 'dev' in combination with the 'rid' uniquely
   68          * identify this interrupt source.
   69          */
   70         device_t dev;
   71         int rid;
   72 
   73         SLIST_ENTRY(sb_intmap) next;
   74 };
   75 
   76 static SLIST_HEAD(, sb_intmap) sb_intmap_head;
   77 
   78 static struct sb_intmap *
   79 sb_intmap_lookup(int intrnum, device_t dev, int rid)
   80 {
   81         struct sb_intmap *map;
   82 
   83         SLIST_FOREACH(map, &sb_intmap_head, next) {
   84                 if (dev == map->dev && rid == map->rid &&
   85                     intrnum == map->hardint)
   86                         break;
   87         }
   88         return (map);
   89 }
   90 
   91 /*
   92  * Keep track of which (dev,rid,hardint) tuple is using the interrupt source.
   93  *
   94  * We don't actually unmask the interrupt source until the device calls
   95  * a bus_setup_intr() on the resource.
   96  */
   97 static void
   98 sb_intmap_add(int intrnum, device_t dev, int rid, int intsrc)
   99 {
  100         struct sb_intmap *map;
  101         
  102         KASSERT(intrnum >= 0 && intrnum < NHARD_IRQS,
  103                 ("intrnum is out of range: %d", intrnum));
  104 
  105         map = sb_intmap_lookup(intrnum, dev, rid);
  106         if (map) {
  107                 KASSERT(intsrc == map->intsrc,
  108                         ("%s%d allocating SYS_RES_IRQ resource with rid %d "
  109                          "with a different intsrc (%d versus %d)",
  110                         device_get_name(dev), device_get_unit(dev), rid,
  111                         intsrc, map->intsrc));
  112                 return;
  113         }
  114 
  115         map = malloc(sizeof(*map), M_INTMAP, M_WAITOK | M_ZERO);
  116         map->intsrc = intsrc;
  117         map->hardint = intrnum;
  118         map->dev = dev;
  119         map->rid = rid;
  120 
  121         SLIST_INSERT_HEAD(&sb_intmap_head, map, next);
  122 }
  123 
  124 static void
  125 sb_intmap_activate(int intrnum, device_t dev, int rid)
  126 {
  127         struct sb_intmap *map;
  128         
  129         KASSERT(intrnum >= 0 && intrnum < NHARD_IRQS,
  130                 ("intrnum is out of range: %d", intrnum));
  131 
  132         map = sb_intmap_lookup(intrnum, dev, rid);
  133         if (map) {
  134                 /*
  135                  * Deliver all interrupts to CPU0.
  136                  */
  137                 mtx_lock_spin(&zbbus_intr_mtx);
  138                 hardint_to_intsrc_mask[intrnum] |= 1ULL << map->intsrc;
  139                 sb_enable_intsrc(0, map->intsrc);
  140                 mtx_unlock_spin(&zbbus_intr_mtx);
  141         } else {
  142                 /*
  143                  * In zbbus_setup_intr() we blindly call sb_intmap_activate()
  144                  * for every interrupt activation that comes our way.
  145                  *
  146                  * We might end up here if we did not "hijack" the SYS_RES_IRQ
  147                  * resource in zbbus_alloc_resource().
  148                  */
  149                 printf("sb_intmap_activate: unable to activate interrupt %d "
  150                        "for device %s%d rid %d.\n", intrnum,
  151                        device_get_name(dev), device_get_unit(dev), rid);
  152         }
  153 }
  154 
  155 /*
  156  * Replace the default interrupt mask and unmask routines in intr_machdep.c
  157  * with routines that are SMP-friendly. In contrast to the default mask/unmask
  158  * routines in intr_machdep.c these routines do not change the SR.int_mask bits.
  159  *
  160  * Instead they use the interrupt mapper to either mask or unmask all
  161  * interrupt sources feeding into a particular interrupt line of the processor.
  162  *
  163  * This means that these routines have an identical effect irrespective of
  164  * which cpu is executing them. This is important because the ithread may
  165  * be scheduled to run on either of the cpus.
  166  */
  167 static void
  168 zbbus_intr_mask(void *arg)
  169 {
  170         uint64_t mask;
  171         int irq;
  172         
  173         irq = (uintptr_t)arg;
  174 
  175         mtx_lock_spin(&zbbus_intr_mtx);
  176 
  177         mask = sb_read_intsrc_mask(0);
  178         mask |= hardint_to_intsrc_mask[irq];
  179         sb_write_intsrc_mask(0, mask);
  180 
  181         mtx_unlock_spin(&zbbus_intr_mtx);
  182 }
  183 
  184 static void
  185 zbbus_intr_unmask(void *arg)
  186 {
  187         uint64_t mask;
  188         int irq;
  189         
  190         irq = (uintptr_t)arg;
  191 
  192         mtx_lock_spin(&zbbus_intr_mtx);
  193 
  194         mask = sb_read_intsrc_mask(0);
  195         mask &= ~hardint_to_intsrc_mask[irq];
  196         sb_write_intsrc_mask(0, mask);
  197 
  198         mtx_unlock_spin(&zbbus_intr_mtx);
  199 }
  200 
  201 struct zbbus_devinfo {
  202         struct resource_list resources;
  203 };
  204 
  205 static MALLOC_DEFINE(M_ZBBUSDEV, "zbbusdev", "zbbusdev");
  206 
  207 static int
  208 zbbus_probe(device_t dev)
  209 {
  210 
  211         device_set_desc(dev, "Broadcom/Sibyte ZBbus");
  212         return (BUS_PROBE_NOWILDCARD);
  213 }
  214 
  215 static int
  216 zbbus_attach(device_t dev)
  217 {
  218 
  219         if (bootverbose) {
  220                 device_printf(dev, "attached.\n");
  221         }
  222 
  223         cpu_set_hardintr_mask_func(zbbus_intr_mask);
  224         cpu_set_hardintr_unmask_func(zbbus_intr_unmask);
  225 
  226         bus_generic_probe(dev);
  227         bus_enumerate_hinted_children(dev);
  228         bus_generic_attach(dev);
  229 
  230         return (0);
  231 }
  232 
  233 static void
  234 zbbus_hinted_child(device_t bus, const char *dname, int dunit)
  235 {
  236         device_t child;
  237         long maddr, msize;
  238         int err, irq;
  239 
  240         if (resource_disabled(dname, dunit))
  241                 return;
  242 
  243         child = BUS_ADD_CHILD(bus, 0, dname, dunit);
  244         if (child == NULL) {
  245                 panic("zbbus: could not add child %s unit %d\n", dname, dunit);
  246         }
  247 
  248         if (bootverbose)
  249                 device_printf(bus, "Adding hinted child %s%d\n", dname, dunit);
  250 
  251         /*
  252          * Assign any pre-defined resources to the child.
  253          */
  254         if (resource_long_value(dname, dunit, "msize", &msize) == 0 &&
  255             resource_long_value(dname, dunit, "maddr", &maddr) == 0) {
  256                 if (bootverbose) {
  257                         device_printf(bus, "Assigning memory resource "
  258                                            "0x%0lx/%ld to child %s%d\n",
  259                                            maddr, msize, dname, dunit);
  260                 }
  261                 err = bus_set_resource(child, SYS_RES_MEMORY, 0, maddr, msize);
  262                 if (err) {
  263                         device_printf(bus, "Unable to set memory resource "
  264                                            "0x%0lx/%ld for child %s%d: %d\n",
  265                                            maddr, msize, dname, dunit, err);
  266                 }
  267         }
  268 
  269         if (resource_int_value(dname, dunit, "irq", &irq) == 0) {
  270                 if (bootverbose) {
  271                         device_printf(bus, "Assigning irq resource %d to "
  272                                            "child %s%d\n", irq, dname, dunit);
  273                 }
  274                 err = bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1);
  275                 if (err) {
  276                         device_printf(bus, "Unable to set irq resource %d"
  277                                            "for child %s%d: %d\n",
  278                                            irq, dname, dunit, err);
  279                 }
  280         }
  281 }
  282 
  283 static struct resource *
  284 zbbus_alloc_resource(device_t bus, device_t child, int type, int *rid,
  285                      rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
  286 {
  287         struct resource *res;
  288         int intrnum, intsrc, isdefault;
  289         struct resource_list *rl;
  290         struct resource_list_entry *rle;
  291         struct zbbus_devinfo *dinfo;
  292 
  293         isdefault = (RMAN_IS_DEFAULT_RANGE(start, end) && count == 1);
  294 
  295         /*
  296          * Our direct child is asking for a default resource allocation.
  297          */
  298         if (device_get_parent(child) == bus) {
  299                 dinfo = device_get_ivars(child);
  300                 rl = &dinfo->resources;
  301                 rle = resource_list_find(rl, type, *rid);
  302                 if (rle) {
  303                         if (rle->res)
  304                                 panic("zbbus_alloc_resource: resource is busy");
  305                         if (isdefault) {
  306                                 start = rle->start;
  307                                 count = ulmax(count, rle->count);
  308                                 end = ulmax(rle->end, start + count - 1);
  309                         }
  310                 } else {
  311                         if (isdefault) {
  312                                 /*
  313                                  * Our child is requesting a default
  314                                  * resource allocation but we don't have the
  315                                  * 'type/rid' tuple in the resource list.
  316                                  *
  317                                  * We have to fail the resource allocation.
  318                                  */
  319                                 return (NULL);
  320                         } else {
  321                                 /*
  322                                  * The child is requesting a non-default
  323                                  * resource. We just pass the request up
  324                                  * to our parent. If the resource allocation
  325                                  * succeeds we will create a resource list
  326                                  * entry corresponding to that resource.
  327                                  */
  328                         }
  329                 }
  330         } else {
  331                 rl = NULL;
  332                 rle = NULL;
  333         }
  334 
  335         /*
  336          * nexus doesn't know about the interrupt mapper and only wants to
  337          * see the hard irq numbers [0-6]. We translate from the interrupt
  338          * source presented to the mapper to the interrupt number presented
  339          * to the cpu.
  340          */
  341         if ((count == 1) && (type == SYS_RES_IRQ)) {
  342                 intsrc = start;
  343                 intrnum = sb_route_intsrc(intsrc);
  344                 start = end = intrnum;
  345         } else {
  346                 intsrc = -1;            /* satisfy gcc */
  347                 intrnum = -1;
  348         }
  349 
  350         res = bus_generic_alloc_resource(bus, child, type, rid,
  351                                          start, end, count, flags);
  352 
  353         /*
  354          * Keep track of the input into the interrupt mapper that maps
  355          * to the resource allocated by 'child' with resource id 'rid'.
  356          *
  357          * If we don't record the mapping here then we won't be able to
  358          * locate the interrupt source when bus_setup_intr(child,rid) is
  359          * called.
  360          */
  361         if (res != NULL && intrnum != -1)
  362                 sb_intmap_add(intrnum, child, rman_get_rid(res), intsrc);
  363 
  364         /*
  365          * If a non-default resource allocation by our child was successful
  366          * then keep track of the resource in the resource list associated
  367          * with the child.
  368          */
  369         if (res != NULL && rle == NULL && device_get_parent(child) == bus) {
  370                 resource_list_add(rl, type, *rid, start, end, count);
  371                 rle = resource_list_find(rl, type, *rid);
  372                 if (rle == NULL)
  373                         panic("zbbus_alloc_resource: cannot find resource");
  374         }
  375 
  376         if (rle != NULL) {
  377                 KASSERT(device_get_parent(child) == bus,
  378                         ("rle should be NULL for passthru device"));
  379                 rle->res = res;
  380                 if (rle->res) {
  381                         rle->start = rman_get_start(rle->res);
  382                         rle->end = rman_get_end(rle->res);
  383                         rle->count = count;
  384                 }
  385         }
  386 
  387         return (res);
  388 }
  389 
  390 static int
  391 zbbus_setup_intr(device_t dev, device_t child, struct resource *irq, int flags,
  392                  driver_filter_t *filter, driver_intr_t *intr, void *arg, 
  393                  void **cookiep)
  394 {
  395         int error;
  396 
  397         error = bus_generic_setup_intr(dev, child, irq, flags,
  398                                        filter, intr, arg, cookiep);
  399         if (error == 0)
  400                 sb_intmap_activate(rman_get_start(irq), child,
  401                                    rman_get_rid(irq));
  402 
  403         return (error);
  404 }
  405 
  406 static device_t
  407 zbbus_add_child(device_t bus, u_int order, const char *name, int unit)
  408 {
  409         device_t child;
  410         struct zbbus_devinfo *dinfo;
  411 
  412         child = device_add_child_ordered(bus, order, name, unit);
  413         if (child != NULL) {
  414                 dinfo = malloc(sizeof(struct zbbus_devinfo), M_ZBBUSDEV,
  415                                M_WAITOK | M_ZERO);
  416                 resource_list_init(&dinfo->resources);
  417                 device_set_ivars(child, dinfo);
  418         }
  419 
  420         return (child);
  421 }
  422 
  423 static struct resource_list *
  424 zbbus_get_resource_list(device_t dev, device_t child)
  425 {
  426         struct zbbus_devinfo *dinfo = device_get_ivars(child);
  427 
  428         return (&dinfo->resources);
  429 }
  430 
  431 static device_method_t zbbus_methods[] ={
  432         /* Device interface */
  433         DEVMETHOD(device_probe,         zbbus_probe),
  434         DEVMETHOD(device_attach,        zbbus_attach),
  435         DEVMETHOD(device_detach,        bus_generic_detach),
  436         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
  437         DEVMETHOD(device_suspend,       bus_generic_suspend),
  438         DEVMETHOD(device_resume,        bus_generic_resume),
  439 
  440         /* Bus interface */
  441         DEVMETHOD(bus_alloc_resource,   zbbus_alloc_resource),
  442         DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
  443         DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
  444         DEVMETHOD(bus_release_resource, bus_generic_release_resource),
  445         DEVMETHOD(bus_get_resource_list,zbbus_get_resource_list),
  446         DEVMETHOD(bus_set_resource,     bus_generic_rl_set_resource),
  447         DEVMETHOD(bus_get_resource,     bus_generic_rl_get_resource),
  448         DEVMETHOD(bus_delete_resource,  bus_generic_rl_delete_resource),
  449         DEVMETHOD(bus_setup_intr,       zbbus_setup_intr),
  450         DEVMETHOD(bus_teardown_intr,    bus_generic_teardown_intr),
  451         DEVMETHOD(bus_add_child,        zbbus_add_child),
  452         DEVMETHOD(bus_hinted_child,     zbbus_hinted_child),
  453         
  454         { 0, 0 }
  455 };
  456 
  457 static driver_t zbbus_driver = {
  458         "zbbus",
  459         zbbus_methods
  460 };
  461 
  462 static devclass_t zbbus_devclass;
  463 
  464 DRIVER_MODULE(zbbus, nexus, zbbus_driver, zbbus_devclass, 0, 0);

Cache object: 364fb0392d236d3874e409067384ce73


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.