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/dev/mailbox/arm/arm_doorbell.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
    3  *
    4  * Copyright (c) 2022 Ruslan Bukin <br@bsdpad.com>
    5  *
    6  * This work was supported by Innovate UK project 105694, "Digital Security
    7  * by Design (DSbD) Technology Platform Prototype".
    8  *
    9  * Redistribution and use in source and binary forms, with or without
   10  * modification, are permitted provided that the following conditions
   11  * are met:
   12  * 1. Redistributions of source code must retain the above copyright
   13  *    notice, this list of conditions and the following disclaimer.
   14  * 2. Redistributions in binary form must reproduce the above copyright
   15  *    notice, this list of conditions and the following disclaimer in the
   16  *    documentation and/or other materials provided with the distribution.
   17  *
   18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   28  * SUCH DAMAGE.
   29  */
   30 
   31 #include <sys/cdefs.h>
   32 __FBSDID("$FreeBSD$");
   33 
   34 #include <sys/param.h>
   35 #include <sys/systm.h>
   36 #include <sys/bus.h>
   37 #include <sys/rman.h>
   38 #include <sys/kernel.h>
   39 #include <sys/module.h>
   40 
   41 #include <machine/bus.h>
   42 
   43 #include <dev/fdt/simplebus.h>
   44 #include <dev/fdt/fdt_common.h>
   45 #include <dev/ofw/ofw_bus_subr.h>
   46 
   47 #include "arm_doorbell.h"
   48 
   49 #define MHU_CHAN_RX_LP          0x000   /* Low priority channel */
   50 #define MHU_CHAN_RX_HP          0x020   /* High priority channel */
   51 #define MHU_CHAN_RX_SEC         0x200   /* Secure channel */
   52 #define  MHU_INTR_STAT          0x00
   53 #define  MHU_INTR_SET           0x08
   54 #define  MHU_INTR_CLEAR         0x10
   55 
   56 #define MHU_TX_REG_OFFSET       0x100
   57 
   58 #define DOORBELL_N_CHANNELS     3
   59 #define DOORBELL_N_DOORBELLS    (DOORBELL_N_CHANNELS * 32)
   60 
   61 struct arm_doorbell dbells[DOORBELL_N_DOORBELLS];
   62 
   63 static struct resource_spec arm_doorbell_spec[] = {
   64         { SYS_RES_MEMORY,       0,      RF_ACTIVE },
   65         { SYS_RES_IRQ,          0,      RF_ACTIVE },
   66         { SYS_RES_IRQ,          1,      RF_ACTIVE },
   67         { -1, 0 }
   68 };
   69 
   70 struct arm_doorbell_softc {
   71         struct resource         *res[3];
   72         void                    *lp_intr_cookie;
   73         void                    *hp_intr_cookie;
   74         device_t                dev;
   75 };
   76 
   77 static void
   78 arm_doorbell_lp_intr(void *arg)
   79 {
   80         struct arm_doorbell_softc *sc;
   81         struct arm_doorbell *db;
   82         uint32_t reg;
   83         int i;
   84 
   85         sc = arg;
   86 
   87         reg = bus_read_4(sc->res[0], MHU_CHAN_RX_LP + MHU_INTR_STAT);
   88         for (i = 0; i < 32; i++) {
   89                 if (reg & (1 << i)) {
   90                         db = &dbells[i];
   91                         bus_write_4(sc->res[0], MHU_CHAN_RX_LP + MHU_INTR_CLEAR,
   92                             (1 << i));
   93                         if (db->func != NULL)
   94                                 db->func(db->arg);
   95                 }
   96         }
   97 }
   98 
   99 static void
  100 arm_doorbell_hp_intr(void *arg)
  101 {
  102         struct arm_doorbell_softc *sc;
  103         struct arm_doorbell *db;
  104         uint32_t reg;
  105         int i;
  106 
  107         sc = arg;
  108 
  109         reg = bus_read_4(sc->res[0], MHU_CHAN_RX_HP + MHU_INTR_STAT);
  110         for (i = 0; i < 32; i++) {
  111                 if (reg & (1 << i)) {
  112                         db = &dbells[i];
  113                         bus_write_4(sc->res[0], MHU_CHAN_RX_HP + MHU_INTR_CLEAR,
  114                             (1 << i));
  115                         if (db->func != NULL)
  116                                 db->func(db->arg);
  117                 }
  118         }
  119 }
  120 
  121 static int
  122 arm_doorbell_probe(device_t dev)
  123 {
  124 
  125         if (!ofw_bus_is_compatible(dev, "arm,mhu-doorbell"))
  126                 return (ENXIO);
  127 
  128         if (!ofw_bus_status_okay(dev))
  129                 return (ENXIO);
  130 
  131         device_set_desc(dev, "ARM MHU Doorbell");
  132 
  133         return (BUS_PROBE_DEFAULT);
  134 }
  135 
  136 static int
  137 arm_doorbell_attach(device_t dev)
  138 {
  139         struct arm_doorbell_softc *sc;
  140         phandle_t node;
  141         int error;
  142 
  143         sc = device_get_softc(dev);
  144         sc->dev = dev;
  145 
  146         node = ofw_bus_get_node(dev);
  147         if (node == -1)
  148                 return (ENXIO);
  149 
  150         if (bus_alloc_resources(dev, arm_doorbell_spec, sc->res) != 0) {
  151                 device_printf(dev, "Can't allocate resources for device.\n");
  152                 return (ENXIO);
  153         }
  154 
  155         /* Setup interrupt handlers. */
  156         error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE,
  157             NULL, arm_doorbell_lp_intr, sc, &sc->lp_intr_cookie);
  158         if (error != 0) {
  159                 device_printf(dev, "Can't setup LP interrupt handler.\n");
  160                 bus_release_resources(dev, arm_doorbell_spec, sc->res);
  161                 return (ENXIO);
  162         }
  163 
  164         error = bus_setup_intr(dev, sc->res[2], INTR_TYPE_MISC | INTR_MPSAFE,
  165             NULL, arm_doorbell_hp_intr, sc, &sc->hp_intr_cookie);
  166         if (error != 0) {
  167                 device_printf(dev, "Can't setup HP interrupt handler.\n");
  168                 bus_release_resources(dev, arm_doorbell_spec, sc->res);
  169                 return (ENXIO);
  170         }
  171 
  172         OF_device_register_xref(OF_xref_from_node(node), dev);
  173 
  174         return (0);
  175 }
  176 
  177 static int
  178 arm_doorbell_detach(device_t dev)
  179 {
  180 
  181         return (EBUSY);
  182 }
  183 
  184 struct arm_doorbell *
  185 arm_doorbell_ofw_get(device_t dev, const char *name)
  186 {
  187         phandle_t node, parent;
  188         struct arm_doorbell *db;
  189         device_t db_dev;
  190         pcell_t *cells;
  191         int nmboxes;
  192         int ncells;
  193         int idx;
  194         int db_id;
  195         int error;
  196         int chan;
  197 
  198         node = ofw_bus_get_node(dev);
  199 
  200         error = ofw_bus_parse_xref_list_get_length(node, "mboxes",
  201             "#mbox-cells", &nmboxes);
  202         if (error) {
  203                 device_printf(dev, "%s can't get mboxes list.\n", __func__);
  204                 return (NULL);
  205         }
  206 
  207         if (nmboxes == 0) {
  208                 device_printf(dev, "%s mbox list is empty.\n", __func__);
  209                 return (NULL);
  210         }
  211 
  212         error = ofw_bus_find_string_index(node, "mbox-names", name, &idx);
  213         if (error != 0) {
  214                 device_printf(dev, "%s can't find string index.\n",
  215                     __func__);
  216                 return (NULL);
  217         }
  218 
  219         error = ofw_bus_parse_xref_list_alloc(node, "mboxes", "#mbox-cells",
  220             idx, &parent, &ncells, &cells);
  221         if (error != 0) {
  222                 device_printf(dev, "%s can't get mbox device xref\n",
  223                     __func__);
  224                 return (NULL);
  225         }
  226 
  227         if (ncells != 2) {
  228                 device_printf(dev, "Unexpected data size.\n");
  229                 OF_prop_free(cells);
  230                 return (NULL);
  231         }
  232 
  233         db_dev = OF_device_from_xref(parent);
  234         if (db_dev == NULL) {
  235                 device_printf(dev, "%s: Can't get arm_doorbell device\n",
  236                     __func__);
  237                 OF_prop_free(cells);
  238                 return (NULL);
  239         }
  240 
  241         chan = cells[0];
  242         if (chan >= DOORBELL_N_CHANNELS) {
  243                 device_printf(dev, "Unexpected channel number.\n");
  244                 OF_prop_free(cells);
  245                 return (NULL);
  246         }
  247 
  248         db_id = cells[1];
  249         if (db_id >= 32) {
  250                 device_printf(dev, "Unexpected channel bit.\n");
  251                 OF_prop_free(cells);
  252                 return (NULL);
  253         }
  254 
  255         db = &dbells[chan * db_id];
  256         db->dev = dev;
  257         db->db_dev = db_dev;
  258         db->chan = chan;
  259         db->db = db_id;
  260 
  261         OF_prop_free(cells);
  262 
  263         return (db);
  264 }
  265 
  266 void
  267 arm_doorbell_set(struct arm_doorbell *db)
  268 {
  269         struct arm_doorbell_softc *sc;
  270         uint32_t offset;
  271 
  272         sc = device_get_softc(db->db_dev);
  273 
  274         switch (db->chan) {
  275         case 0:
  276                 offset = MHU_CHAN_RX_LP;
  277                 break;
  278         case 1:
  279                 offset = MHU_CHAN_RX_HP;
  280                 break;
  281         case 2:
  282                 offset = MHU_CHAN_RX_SEC;
  283                 break;
  284         default:
  285                 panic("not reached");
  286         };
  287 
  288         offset |= MHU_TX_REG_OFFSET;
  289 
  290         bus_write_4(sc->res[0], offset + MHU_INTR_SET, (1 << db->db));
  291 }
  292 
  293 int
  294 arm_doorbell_get(struct arm_doorbell *db)
  295 {
  296         struct arm_doorbell_softc *sc;
  297         uint32_t offset;
  298         uint32_t reg;
  299 
  300         sc = device_get_softc(db->db_dev);
  301 
  302         switch (db->chan) {
  303         case 0:
  304                 offset = MHU_CHAN_RX_LP;
  305                 break;
  306         case 1:
  307                 offset = MHU_CHAN_RX_HP;
  308                 break;
  309         case 2:
  310                 offset = MHU_CHAN_RX_SEC;
  311                 break;
  312         default:
  313                 panic("not reached");
  314         };
  315 
  316         reg = bus_read_4(sc->res[0], offset + MHU_INTR_STAT);
  317         if (reg & (1 << db->db)) {
  318                 bus_write_4(sc->res[0], offset + MHU_INTR_CLEAR,
  319                     (1 << db->db));
  320                 return (1);
  321         }
  322 
  323         return (0);
  324 }
  325 
  326 void
  327 arm_doorbell_set_handler(struct arm_doorbell *db, void (*func)(void *),
  328     void *arg)
  329 {
  330 
  331         db->func = func;
  332         db->arg = arg;
  333 }
  334 
  335 static device_method_t arm_doorbell_methods[] = {
  336         DEVMETHOD(device_probe,         arm_doorbell_probe),
  337         DEVMETHOD(device_attach,        arm_doorbell_attach),
  338         DEVMETHOD(device_detach,        arm_doorbell_detach),
  339         DEVMETHOD_END
  340 };
  341 
  342 DEFINE_CLASS_1(arm_doorbell, arm_doorbell_driver, arm_doorbell_methods,
  343     sizeof(struct arm_doorbell_softc), simplebus_driver);
  344 
  345 EARLY_DRIVER_MODULE(arm_doorbell, simplebus, arm_doorbell_driver, 0, 0,
  346     BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
  347 MODULE_VERSION(arm_doorbell, 1);

Cache object: e85bd8b924bde0e72679d3c37cf807e8


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