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/arm/arm/gic_acpi.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  * Copyright (c) 2011,2016 The FreeBSD Foundation
    3  * All rights reserved.
    4  *
    5  * This software was developed by Andrew Turner under
    6  * sponsorship from the FreeBSD Foundation.
    7  *
    8  * Developed by Damjan Marion <damjan.marion@gmail.com>
    9  *
   10  * Based on OMAP4 GIC code by Ben Gray
   11  *
   12  * Redistribution and use in source and binary forms, with or without
   13  * modification, are permitted provided that the following conditions
   14  * are met:
   15  * 1. Redistributions of source code must retain the above copyright
   16  *    notice, this list of conditions and the following disclaimer.
   17  * 2. Redistributions in binary form must reproduce the above copyright
   18  *    notice, this list of conditions and the following disclaimer in the
   19  *    documentation and/or other materials provided with the distribution.
   20  * 3. The name of the company nor the name of the author may be used to
   21  *    endorse or promote products derived from this software without specific
   22  *    prior written permission.
   23  *
   24  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   27  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   34  * SUCH DAMAGE.
   35  */
   36 
   37 #include "opt_acpi.h"
   38 #include "opt_platform.h"
   39 
   40 #include <sys/cdefs.h>
   41 __FBSDID("$FreeBSD$");
   42 
   43 #include <sys/param.h>
   44 #include <sys/systm.h>
   45 #include <sys/bus.h>
   46 #include <sys/kernel.h>
   47 #include <sys/malloc.h>
   48 #include <sys/module.h>
   49 
   50 #include <machine/intr.h>
   51 
   52 #include <contrib/dev/acpica/include/acpi.h>
   53 #include <dev/acpica/acpivar.h>
   54 
   55 #include <arm/arm/gic.h>
   56 #include <arm/arm/gic_common.h>
   57 
   58 struct gic_acpi_devinfo {
   59         struct resource_list    rl;
   60 };
   61 
   62 static device_identify_t gic_acpi_identify;
   63 static device_probe_t gic_acpi_probe;
   64 static device_attach_t gic_acpi_attach;
   65 static bus_get_resource_list_t gic_acpi_get_resource_list;
   66 static bool arm_gic_add_children(device_t);
   67 
   68 static device_method_t gic_acpi_methods[] = {
   69         /* Device interface */
   70         DEVMETHOD(device_identify,      gic_acpi_identify),
   71         DEVMETHOD(device_probe,         gic_acpi_probe),
   72         DEVMETHOD(device_attach,        gic_acpi_attach),
   73 
   74         /* Bus interface */
   75         DEVMETHOD(bus_get_resource_list, gic_acpi_get_resource_list),
   76 
   77         DEVMETHOD_END,
   78 };
   79 
   80 DEFINE_CLASS_1(gic, gic_acpi_driver, gic_acpi_methods,
   81     sizeof(struct arm_gic_softc), arm_gic_driver);
   82 
   83 static devclass_t gic_acpi_devclass;
   84 
   85 EARLY_DRIVER_MODULE(gic, acpi, gic_acpi_driver, gic_acpi_devclass, 0, 0,
   86     BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
   87 
   88 struct madt_table_data {
   89         device_t parent;
   90         ACPI_MADT_GENERIC_DISTRIBUTOR *dist;
   91         ACPI_MADT_GENERIC_INTERRUPT *intr[MAXCPU];
   92 };
   93 
   94 static void
   95 madt_handler(ACPI_SUBTABLE_HEADER *entry, void *arg)
   96 {
   97         struct madt_table_data *madt_data;
   98         ACPI_MADT_GENERIC_INTERRUPT *intr;
   99 
  100         madt_data = (struct madt_table_data *)arg;
  101 
  102         switch(entry->Type) {
  103         case ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR:
  104                 if (madt_data->dist != NULL) {
  105                         if (bootverbose)
  106                                 device_printf(madt_data->parent,
  107                                     "gic: Already have a distributor table");
  108                 } else
  109                         madt_data->dist =
  110                             (ACPI_MADT_GENERIC_DISTRIBUTOR *)entry;
  111                 break;
  112         case ACPI_MADT_TYPE_GENERIC_INTERRUPT:
  113                 intr = (ACPI_MADT_GENERIC_INTERRUPT *)entry;
  114                 if (intr->CpuInterfaceNumber < MAXCPU)
  115                         madt_data->intr[intr->CpuInterfaceNumber] = intr;
  116                 break;
  117         }
  118 }
  119 
  120 static void
  121 gic_acpi_identify(driver_t *driver, device_t parent)
  122 {
  123         struct madt_table_data madt_data;
  124         ACPI_MADT_GENERIC_INTERRUPT *intr;
  125         ACPI_TABLE_MADT *madt;
  126         vm_paddr_t physaddr;
  127         device_t dev;
  128         int i;
  129 
  130         physaddr = acpi_find_table(ACPI_SIG_MADT);
  131         if (physaddr == 0)
  132                 return;
  133 
  134         madt = acpi_map_table(physaddr, ACPI_SIG_MADT);
  135         if (madt == NULL) {
  136                 device_printf(parent, "gic: Unable to map the MADT\n");
  137                 return;
  138         }
  139 
  140         bzero(&madt_data, sizeof(madt_data));
  141         madt_data.parent = parent;
  142         madt_data.dist = NULL;
  143 
  144         acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length,
  145             madt_handler, &madt_data);
  146 
  147         /* Check the version of the GIC we have */
  148         switch (madt_data.dist->Version) {
  149         case ACPI_MADT_GIC_VERSION_NONE:
  150         case ACPI_MADT_GIC_VERSION_V1:
  151         case ACPI_MADT_GIC_VERSION_V2:
  152                 break;
  153         default:
  154                 goto out;
  155         }
  156 
  157         intr = NULL;
  158         for (i = 0; i < MAXCPU; i++) {
  159                 if (madt_data.intr[i] != NULL) {
  160                         if (intr == NULL) {
  161                                 intr = madt_data.intr[i];
  162                         } else if (intr->BaseAddress !=
  163                             madt_data.intr[i]->BaseAddress) {
  164                                 device_printf(parent,
  165 "gic: Not all CPU interfaces at the same address, this may fail\n");
  166                         }
  167                 }
  168         }
  169         if (intr == NULL) {
  170                 device_printf(parent, "gic: No CPU interfaces found\n");
  171                 goto out;
  172         }
  173 
  174         dev = BUS_ADD_CHILD(parent, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE,
  175             "gic", -1);
  176         if (dev == NULL) {
  177                 device_printf(parent, "add gic child failed\n");
  178                 goto out;
  179         }
  180 
  181         BUS_SET_RESOURCE(parent, dev, SYS_RES_MEMORY, 0,
  182             madt_data.dist->BaseAddress, 4 * 1024);
  183         BUS_SET_RESOURCE(parent, dev, SYS_RES_MEMORY, 1,
  184             intr->BaseAddress, 4 * 1024);
  185 
  186         acpi_set_private(dev, (void *)(uintptr_t)madt_data.dist->Version);
  187 out:
  188         acpi_unmap_table(madt);
  189 }
  190 
  191 static int
  192 gic_acpi_probe(device_t dev)
  193 {
  194 
  195         switch((uintptr_t)acpi_get_private(dev)) {
  196         case ACPI_MADT_GIC_VERSION_NONE:
  197         case ACPI_MADT_GIC_VERSION_V1:
  198         case ACPI_MADT_GIC_VERSION_V2:
  199                 break;
  200         default:
  201                 return (ENXIO);
  202         }
  203 
  204         device_set_desc(dev, "ARM Generic Interrupt Controller");
  205         return (BUS_PROBE_NOWILDCARD);
  206 }
  207 
  208 static int
  209 gic_acpi_attach(device_t dev)
  210 {
  211         struct arm_gic_softc *sc = device_get_softc(dev);
  212         intptr_t xref;
  213         int err;
  214 
  215         sc->gic_bus = GIC_BUS_ACPI;
  216 
  217         err = arm_gic_attach(dev);
  218         if (err != 0)
  219                 return (err);
  220 
  221         xref = ACPI_INTR_XREF;
  222 
  223         /*
  224          * Now, when everything is initialized, it's right time to
  225          * register interrupt controller to interrupt framefork.
  226          */
  227         if (intr_pic_register(dev, xref) == NULL) {
  228                 device_printf(dev, "could not register PIC\n");
  229                 goto cleanup;
  230         }
  231 
  232         /*
  233          * Controller is root:
  234          */
  235         if (intr_pic_claim_root(dev, xref, arm_gic_intr, sc,
  236             GIC_LAST_SGI - GIC_FIRST_SGI + 1) != 0) {
  237                 device_printf(dev, "could not set PIC as a root\n");
  238                 intr_pic_deregister(dev, xref);
  239                 goto cleanup;
  240         }
  241         /* If we have children probe and attach them */
  242         if (arm_gic_add_children(dev)) {
  243                 bus_generic_probe(dev);
  244                 return (bus_generic_attach(dev));
  245         }
  246 
  247         return (0);
  248 
  249 cleanup:
  250         arm_gic_detach(dev);
  251         return(ENXIO);
  252 }
  253 
  254 static struct resource_list *
  255 gic_acpi_get_resource_list(device_t bus, device_t child)
  256 {
  257         struct gic_acpi_devinfo *di;
  258 
  259         di = device_get_ivars(child);
  260         KASSERT(di != NULL, ("gic_acpi_get_resource_list: No devinfo"));
  261 
  262         return (&di->rl);
  263 }
  264 
  265 static void
  266 madt_gicv2m_handler(ACPI_SUBTABLE_HEADER *entry, void *arg)
  267 {
  268         struct arm_gic_softc *sc;
  269         ACPI_MADT_GENERIC_MSI_FRAME *msi;
  270         struct gic_acpi_devinfo *dinfo;
  271         device_t dev, cdev;
  272 
  273         if (entry->Type == ACPI_MADT_TYPE_GENERIC_MSI_FRAME) {
  274                 sc = arg;
  275                 dev = sc->gic_dev;
  276                 msi = (ACPI_MADT_GENERIC_MSI_FRAME *)entry;
  277 
  278                 device_printf(dev, "frame: %x %lx %x %u %u\n", msi->MsiFrameId,
  279                     msi->BaseAddress, msi->Flags, msi->SpiCount, msi->SpiBase);
  280 
  281                 cdev = device_add_child(dev, NULL, -1);
  282                 if (cdev == NULL)
  283                         return;
  284 
  285                 dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO);
  286                 resource_list_init(&dinfo->rl);
  287                 resource_list_add(&dinfo->rl, SYS_RES_MEMORY, 0,
  288                     msi->BaseAddress, msi->BaseAddress + PAGE_SIZE - 1,
  289                     PAGE_SIZE);
  290                 device_set_ivars(cdev, dinfo);
  291         }
  292 }
  293 
  294 static bool
  295 arm_gic_add_children(device_t dev)
  296 {
  297         struct arm_gic_softc *sc = device_get_softc(dev);
  298         ACPI_TABLE_MADT *madt;
  299         vm_paddr_t physaddr;
  300 
  301         /* This should return a valid address as it did in gic_acpi_identify */
  302         physaddr = acpi_find_table(ACPI_SIG_MADT);
  303         if (physaddr == 0)
  304                 return (false);
  305 
  306         madt = acpi_map_table(physaddr, ACPI_SIG_MADT);
  307         if (madt == NULL) {
  308                 device_printf(dev, "gic: Unable to map the MADT\n");
  309                 return (false);
  310         }
  311 
  312         acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length,
  313             madt_gicv2m_handler, sc);
  314 
  315         acpi_unmap_table(madt);
  316 
  317         return (true);
  318 }
  319 
  320 static int
  321 arm_gicv2m_acpi_probe(device_t dev)
  322 {
  323 
  324         if (gic_get_bus(dev) != GIC_BUS_ACPI)
  325                 return (EINVAL);
  326 
  327         if (gic_get_hw_rev(dev) > 2)
  328                 return (EINVAL);
  329 
  330         device_set_desc(dev, "ARM Generic Interrupt Controller MSI/MSIX");
  331         return (BUS_PROBE_DEFAULT);
  332 }
  333 
  334 static int
  335 arm_gicv2m_acpi_attach(device_t dev)
  336 {
  337         struct arm_gicv2m_softc *sc;
  338 
  339         sc = device_get_softc(dev);
  340         sc->sc_xref = ACPI_MSI_XREF;
  341 
  342         return (arm_gicv2m_attach(dev));
  343 }
  344 
  345 static device_method_t arm_gicv2m_acpi_methods[] = {
  346         /* Device interface */
  347         DEVMETHOD(device_probe,         arm_gicv2m_acpi_probe),
  348         DEVMETHOD(device_attach,        arm_gicv2m_acpi_attach),
  349 
  350         /* End */
  351         DEVMETHOD_END
  352 };
  353 
  354 DEFINE_CLASS_1(gicv2m, arm_gicv2m_acpi_driver, arm_gicv2m_acpi_methods,
  355     sizeof(struct arm_gicv2m_softc), arm_gicv2m_driver);
  356 
  357 static devclass_t arm_gicv2m_acpi_devclass;
  358 
  359 EARLY_DRIVER_MODULE(gicv2m_acpi, gic, arm_gicv2m_acpi_driver,
  360     arm_gicv2m_acpi_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);

Cache object: 42dcbb5c6af24ef5ed1bf5c8d036958c


[ 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.