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/allwinner/aw_nmi.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) 2016 Emmanuel Vadot <manu@freebsd.org>
    3  *
    4  * Redistribution and use in source and binary forms, with or without
    5  * modification, are permitted provided that the following conditions
    6  * are met:
    7  * 1. Redistributions of source code must retain the above copyright
    8  *    notice, this list of conditions and the following disclaimer.
    9  * 2. Redistributions in binary form must reproduce the above copyright
   10  *    notice, this list of conditions and the following disclaimer in the
   11  *    documentation and/or other materials provided with the distribution.
   12  *
   13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   23  * SUCH DAMAGE.
   24  */
   25 
   26 #include <sys/cdefs.h>
   27 __FBSDID("$FreeBSD$");
   28 
   29 #include "opt_platform.h"
   30 
   31 #include <sys/param.h>
   32 #include <sys/systm.h>
   33 #include <sys/bus.h>
   34 #include <sys/kernel.h>
   35 #include <sys/module.h>
   36 #include <sys/proc.h>
   37 #include <sys/rman.h>
   38 #include <machine/bus.h>
   39 #include <machine/intr.h>
   40 
   41 #include <dev/fdt/fdt_intr.h>
   42 #include <dev/ofw/openfirm.h>
   43 #include <dev/ofw/ofw_bus.h>
   44 #include <dev/ofw/ofw_bus_subr.h>
   45 
   46 #include "pic_if.h"
   47 
   48 #define NMI_IRQ_CTRL_REG        0x0
   49 #define  NMI_IRQ_LOW_LEVEL      0x0
   50 #define  NMI_IRQ_LOW_EDGE       0x1
   51 #define  NMI_IRQ_HIGH_LEVEL     0x2
   52 #define  NMI_IRQ_HIGH_EDGE      0x3
   53 #define NMI_IRQ_PENDING_REG     0x4
   54 #define  NMI_IRQ_ACK            (1U << 0)
   55 #define A20_NMI_IRQ_ENABLE_REG  0x8
   56 #define A31_NMI_IRQ_ENABLE_REG  0x34
   57 #define  NMI_IRQ_ENABLE         (1U << 0)
   58 
   59 #define R_NMI_IRQ_CTRL_REG      0x0c
   60 #define R_NMI_IRQ_PENDING_REG   0x10
   61 #define R_NMI_IRQ_ENABLE_REG    0x40
   62 
   63 #define SC_NMI_READ(_sc, _reg)          bus_read_4(_sc->res[0], _reg)
   64 #define SC_NMI_WRITE(_sc, _reg, _val)   bus_write_4(_sc->res[0], _reg, _val)
   65 
   66 static struct resource_spec aw_nmi_res_spec[] = {
   67         { SYS_RES_MEMORY,       0,      RF_ACTIVE },
   68         { SYS_RES_IRQ,          0,      RF_ACTIVE },
   69         { -1,                   0,      0 }
   70 };
   71 
   72 struct aw_nmi_intr {
   73         struct intr_irqsrc      isrc;
   74         u_int                   irq;
   75         enum intr_polarity      pol;
   76         enum intr_trigger       tri;
   77 };
   78 
   79 struct aw_nmi_reg_cfg {
   80         uint8_t                 ctrl_reg;
   81         uint8_t                 pending_reg;
   82         uint8_t                 enable_reg;
   83 };
   84 
   85 struct aw_nmi_softc {
   86         device_t                dev;
   87         struct resource *       res[2];
   88         void *                  intrcookie;
   89         struct aw_nmi_intr      intr;
   90         struct aw_nmi_reg_cfg * cfg;
   91 };
   92 
   93 static struct aw_nmi_reg_cfg a20_nmi_cfg = {
   94         .ctrl_reg =     NMI_IRQ_CTRL_REG,
   95         .pending_reg =  NMI_IRQ_PENDING_REG,
   96         .enable_reg =   A20_NMI_IRQ_ENABLE_REG,
   97 };
   98 
   99 static struct aw_nmi_reg_cfg a31_nmi_cfg = {
  100         .ctrl_reg =     NMI_IRQ_CTRL_REG,
  101         .pending_reg =  NMI_IRQ_PENDING_REG,
  102         .enable_reg =   A31_NMI_IRQ_ENABLE_REG,
  103 };
  104 
  105 static struct aw_nmi_reg_cfg a83t_r_nmi_cfg = {
  106         .ctrl_reg =     R_NMI_IRQ_CTRL_REG,
  107         .pending_reg =  R_NMI_IRQ_PENDING_REG,
  108         .enable_reg =   R_NMI_IRQ_ENABLE_REG,
  109 };
  110 
  111 static struct ofw_compat_data compat_data[] = {
  112         {"allwinner,sun7i-a20-sc-nmi", (uintptr_t)&a20_nmi_cfg},
  113         {"allwinner,sun6i-a31-sc-nmi", (uintptr_t)&a31_nmi_cfg},
  114         {"allwinner,sun6i-a31-r-intc", (uintptr_t)&a83t_r_nmi_cfg},
  115         {"allwinner,sun8i-a83t-r-intc", (uintptr_t)&a83t_r_nmi_cfg},
  116         {NULL, 0},
  117 };
  118 
  119 static int
  120 aw_nmi_intr(void *arg)
  121 {
  122         struct aw_nmi_softc *sc;
  123 
  124         sc = arg;
  125 
  126         if (SC_NMI_READ(sc, sc->cfg->pending_reg) == 0) {
  127                 device_printf(sc->dev, "Spurious interrupt\n");
  128                 return (FILTER_HANDLED);
  129         }
  130 
  131         if (intr_isrc_dispatch(&sc->intr.isrc, curthread->td_intr_frame) != 0) {
  132                 SC_NMI_WRITE(sc, sc->cfg->enable_reg, ~NMI_IRQ_ENABLE);
  133                 device_printf(sc->dev, "Stray interrupt, NMI disabled\n");
  134         }
  135 
  136         return (FILTER_HANDLED);
  137 }
  138 
  139 static void
  140 aw_nmi_enable_intr(device_t dev, struct intr_irqsrc *isrc)
  141 {
  142         struct aw_nmi_softc *sc;
  143 
  144         sc = device_get_softc(dev);
  145 
  146         SC_NMI_WRITE(sc, sc->cfg->enable_reg, NMI_IRQ_ENABLE);
  147 }
  148 
  149 static void
  150 aw_nmi_disable_intr(device_t dev, struct intr_irqsrc *isrc)
  151 {
  152         struct aw_nmi_softc *sc;
  153 
  154         sc = device_get_softc(dev);
  155 
  156         SC_NMI_WRITE(sc, sc->cfg->enable_reg, ~NMI_IRQ_ENABLE);
  157 }
  158 
  159 static int
  160 aw_nmi_map_fdt(device_t dev, u_int ncells, pcell_t *cells, u_int *irqp,
  161     enum intr_polarity *polp, enum intr_trigger *trigp)
  162 {
  163         u_int irq, tripol;
  164         enum intr_polarity pol;
  165         enum intr_trigger trig;
  166 
  167         if (ncells != 2) {
  168                 device_printf(dev, "Invalid #interrupt-cells\n");
  169                 return (EINVAL);
  170         }
  171 
  172         irq = cells[0];
  173         if (irq != 0) {
  174                 device_printf(dev, "Controller only support irq 0\n");
  175                 return (EINVAL);
  176         }
  177 
  178         tripol = cells[1];
  179 
  180         switch (tripol) {
  181         case FDT_INTR_EDGE_RISING:
  182                 trig = INTR_TRIGGER_EDGE;
  183                 pol  = INTR_POLARITY_HIGH;
  184                 break;
  185         case FDT_INTR_EDGE_FALLING:
  186                 trig = INTR_TRIGGER_EDGE;
  187                 pol  = INTR_POLARITY_LOW;
  188                 break;
  189         case FDT_INTR_LEVEL_HIGH:
  190                 trig = INTR_TRIGGER_LEVEL;
  191                 pol  = INTR_POLARITY_HIGH;
  192                 break;
  193         case FDT_INTR_LEVEL_LOW:
  194                 trig = INTR_TRIGGER_LEVEL;
  195                 pol  = INTR_POLARITY_LOW;
  196                 break;
  197         default:
  198                 device_printf(dev, "unsupported trigger/polarity 0x%2x\n",
  199                     tripol);
  200                 return (ENOTSUP);
  201         }
  202 
  203         *irqp = irq;
  204         if (polp != NULL)
  205                 *polp = pol;
  206         if (trigp != NULL)
  207                 *trigp = trig;
  208         return (0);
  209 }
  210 
  211 static int
  212 aw_nmi_map_intr(device_t dev, struct intr_map_data *data,
  213     struct intr_irqsrc **isrcp)
  214 {
  215         struct intr_map_data_fdt *daf;
  216         struct aw_nmi_softc *sc;
  217         int error;
  218         u_int irq;
  219 
  220         if (data->type != INTR_MAP_DATA_FDT)
  221                 return (ENOTSUP);
  222 
  223         sc = device_get_softc(dev);
  224         daf = (struct intr_map_data_fdt *)data;
  225 
  226         error = aw_nmi_map_fdt(dev, daf->ncells, daf->cells, &irq, NULL, NULL);
  227         if (error == 0)
  228                 *isrcp = &sc->intr.isrc;
  229 
  230         return (error);
  231 }
  232 
  233 static int
  234 aw_nmi_setup_intr(device_t dev, struct intr_irqsrc *isrc,
  235     struct resource *res, struct intr_map_data *data)
  236 {
  237         struct intr_map_data_fdt *daf;
  238         struct aw_nmi_softc *sc;
  239         struct aw_nmi_intr *nmi_intr;
  240         int error, icfg;
  241         u_int irq;
  242         enum intr_trigger trig;
  243         enum intr_polarity pol;
  244 
  245         /* Get config for interrupt. */
  246         if (data == NULL || data->type != INTR_MAP_DATA_FDT)
  247                 return (ENOTSUP);
  248 
  249         sc = device_get_softc(dev);
  250         nmi_intr = (struct aw_nmi_intr *)isrc;
  251         daf = (struct intr_map_data_fdt *)data;
  252 
  253         error = aw_nmi_map_fdt(dev, daf->ncells, daf->cells, &irq, &pol, &trig);
  254         if (error != 0)
  255                 return (error);
  256         if (nmi_intr->irq != irq)
  257                 return (EINVAL);
  258 
  259         /* Compare config if this is not first setup. */
  260         if (isrc->isrc_handlers != 0) {
  261                 if (pol != nmi_intr->pol || trig != nmi_intr->tri)
  262                         return (EINVAL);
  263                 else
  264                         return (0);
  265         }
  266 
  267         nmi_intr->pol = pol;
  268         nmi_intr->tri = trig;
  269 
  270         if (trig == INTR_TRIGGER_LEVEL) {
  271                 if (pol == INTR_POLARITY_LOW)
  272                         icfg = NMI_IRQ_LOW_LEVEL;
  273                 else
  274                         icfg = NMI_IRQ_HIGH_LEVEL;
  275         } else {
  276                 if (pol == INTR_POLARITY_HIGH)
  277                         icfg = NMI_IRQ_HIGH_EDGE;
  278                 else
  279                         icfg = NMI_IRQ_LOW_EDGE;
  280         }
  281 
  282         SC_NMI_WRITE(sc, sc->cfg->ctrl_reg, icfg);
  283 
  284         return (0);
  285 }
  286 
  287 static int
  288 aw_nmi_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
  289     struct resource *res, struct intr_map_data *data)
  290 {
  291         struct aw_nmi_softc *sc;
  292 
  293         sc = device_get_softc(dev);
  294 
  295         if (isrc->isrc_handlers == 0) {
  296                 sc->intr.pol = INTR_POLARITY_CONFORM;
  297                 sc->intr.tri = INTR_TRIGGER_CONFORM;
  298 
  299                 SC_NMI_WRITE(sc, sc->cfg->enable_reg, ~NMI_IRQ_ENABLE);
  300         }
  301 
  302         return (0);
  303 }
  304 
  305 static void
  306 aw_nmi_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
  307 {
  308         struct aw_nmi_softc *sc;
  309 
  310         sc = device_get_softc(dev);
  311         aw_nmi_disable_intr(dev, isrc);
  312         SC_NMI_WRITE(sc, sc->cfg->pending_reg, NMI_IRQ_ACK);
  313 }
  314 
  315 static void
  316 aw_nmi_post_ithread(device_t dev, struct intr_irqsrc *isrc)
  317 {
  318 
  319         arm_irq_memory_barrier(0);
  320         aw_nmi_enable_intr(dev, isrc);
  321 }
  322 
  323 static void
  324 aw_nmi_post_filter(device_t dev, struct intr_irqsrc *isrc)
  325 {
  326         struct aw_nmi_softc *sc;
  327 
  328         sc = device_get_softc(dev);
  329 
  330         arm_irq_memory_barrier(0);
  331         SC_NMI_WRITE(sc, sc->cfg->pending_reg, NMI_IRQ_ACK);
  332 }
  333 
  334 static int
  335 aw_nmi_probe(device_t dev)
  336 {
  337 
  338         if (!ofw_bus_status_okay(dev))
  339                 return (ENXIO);
  340 
  341         if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
  342                 return (ENXIO);
  343         device_set_desc(dev, "Allwinner NMI Controller");
  344         return (BUS_PROBE_DEFAULT);
  345 }
  346 
  347 static int
  348 aw_nmi_attach(device_t dev)
  349 {
  350         struct aw_nmi_softc *sc;
  351         phandle_t xref;
  352 
  353         sc = device_get_softc(dev);
  354         sc->dev = dev;
  355         sc->cfg = (struct aw_nmi_reg_cfg *)
  356             ofw_bus_search_compatible(dev, compat_data)->ocd_data;
  357 
  358         if (bus_alloc_resources(dev, aw_nmi_res_spec, sc->res) != 0) {
  359                 device_printf(dev, "can't allocate device resources\n");
  360                 return (ENXIO);
  361         }
  362         if ((bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC,
  363             aw_nmi_intr, NULL, sc, &sc->intrcookie))) {
  364                 device_printf(dev, "unable to register interrupt handler\n");
  365                 bus_release_resources(dev, aw_nmi_res_spec, sc->res);
  366                 return (ENXIO);
  367         }
  368 
  369         /* Disable and clear interrupts */
  370         SC_NMI_WRITE(sc, sc->cfg->enable_reg, ~NMI_IRQ_ENABLE);
  371         SC_NMI_WRITE(sc, sc->cfg->pending_reg, NMI_IRQ_ACK);
  372 
  373         xref = OF_xref_from_node(ofw_bus_get_node(dev));
  374         /* Register our isrc */
  375         sc->intr.irq = 0;
  376         sc->intr.pol = INTR_POLARITY_CONFORM;
  377         sc->intr.tri = INTR_TRIGGER_CONFORM;
  378         if (intr_isrc_register(&sc->intr.isrc, sc->dev, 0, "%s,%u",
  379               device_get_nameunit(sc->dev), sc->intr.irq) != 0)
  380                 goto error;
  381 
  382         if (intr_pic_register(dev, (intptr_t)xref) == NULL) {
  383                 device_printf(dev, "could not register pic\n");
  384                 goto error;
  385         }
  386         return (0);
  387 
  388 error:
  389         bus_teardown_intr(dev, sc->res[1], sc->intrcookie);
  390         bus_release_resources(dev, aw_nmi_res_spec, sc->res);
  391         return (ENXIO);
  392 }
  393 
  394 static device_method_t aw_nmi_methods[] = {
  395         DEVMETHOD(device_probe,         aw_nmi_probe),
  396         DEVMETHOD(device_attach,        aw_nmi_attach),
  397 
  398         /* Interrupt controller interface */
  399         DEVMETHOD(pic_disable_intr,     aw_nmi_disable_intr),
  400         DEVMETHOD(pic_enable_intr,      aw_nmi_enable_intr),
  401         DEVMETHOD(pic_map_intr,         aw_nmi_map_intr),
  402         DEVMETHOD(pic_setup_intr,       aw_nmi_setup_intr),
  403         DEVMETHOD(pic_teardown_intr,    aw_nmi_teardown_intr),
  404         DEVMETHOD(pic_post_filter,      aw_nmi_post_filter),
  405         DEVMETHOD(pic_post_ithread,     aw_nmi_post_ithread),
  406         DEVMETHOD(pic_pre_ithread,      aw_nmi_pre_ithread),
  407 
  408         {0, 0},
  409 };
  410 
  411 static driver_t aw_nmi_driver = {
  412         "aw_nmi",
  413         aw_nmi_methods,
  414         sizeof(struct aw_nmi_softc),
  415 };
  416 
  417 static devclass_t aw_nmi_devclass;
  418 
  419 EARLY_DRIVER_MODULE(aw_nmi, simplebus, aw_nmi_driver,
  420     aw_nmi_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);

Cache object: 4088adb2c3cef702134fcb66f31b4788


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