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/bhnd/cores/chipc/chipc_subr.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) 2016 Michael Zhilin <mizhka@gmail.com>
    5  * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
    6  * All rights reserved.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer,
   13  *    without modification.
   14  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
   15  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
   16  *    redistribution must be conditioned upon including a substantially
   17  *    similar Disclaimer requirement for further binary redistribution.
   18  *
   19  * NO WARRANTY
   20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   22  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
   23  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
   24  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
   25  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
   28  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
   30  * THE POSSIBILITY OF SUCH DAMAGES.
   31  */
   32 
   33 #include <sys/cdefs.h>
   34 __FBSDID("$FreeBSD$");
   35 
   36 #include <sys/param.h>
   37 #include <sys/kernel.h>
   38 
   39 #include "chipc_private.h"
   40 #include "chipcvar.h"
   41 
   42 /**
   43  * Return a human-readable name for the given flash @p type.
   44  */
   45 const char *
   46 chipc_flash_name(chipc_flash type)
   47 {
   48         switch (type) {
   49         case CHIPC_PFLASH_CFI:
   50                 return ("CFI Flash");
   51 
   52         case CHIPC_SFLASH_ST:
   53         case CHIPC_SFLASH_AT:
   54                 return ("SPI Flash");
   55 
   56         case CHIPC_QSFLASH_ST:
   57         case CHIPC_QSFLASH_AT:
   58                 return ("QSPI Flash");
   59 
   60         case CHIPC_NFLASH:
   61         case CHIPC_NFLASH_4706:
   62                 return ("NAND");
   63 
   64         case CHIPC_FLASH_NONE:
   65         default:
   66                 return ("unknown");
   67         }
   68 }
   69 
   70 /**
   71  * Return the name of the bus device class used by flash @p type,
   72  * or NULL if @p type is unsupported.
   73  */
   74 const char *
   75 chipc_flash_bus_name(chipc_flash type)
   76 {
   77         switch (type) {
   78         case CHIPC_PFLASH_CFI:
   79                 return ("cfi");
   80 
   81         case CHIPC_SFLASH_ST:
   82         case CHIPC_SFLASH_AT:
   83                 return ("spi");
   84 
   85         case CHIPC_QSFLASH_ST:
   86         case CHIPC_QSFLASH_AT:
   87                 /* unimplemented; spi? */
   88                 return (NULL);
   89 
   90         case CHIPC_NFLASH:
   91         case CHIPC_NFLASH_4706:
   92                 /* unimplemented; nandbus? */
   93                 return (NULL);
   94 
   95         case CHIPC_FLASH_NONE:
   96         default:
   97                 return (NULL);
   98         }
   99 }
  100 
  101 /**
  102  * Return the name of the flash device class for SPI flash @p type,
  103  * or NULL if @p type does not use SPI, or is unsupported.
  104  */
  105 const char *
  106 chipc_sflash_device_name(chipc_flash type)
  107 {
  108         switch (type) {
  109         case CHIPC_SFLASH_ST:
  110                 return ("mx25l");
  111 
  112         case CHIPC_SFLASH_AT:
  113                 return ("at45d");
  114 
  115         case CHIPC_QSFLASH_ST:
  116         case CHIPC_QSFLASH_AT:
  117                 /* unimplemented */
  118                 return (NULL);
  119 
  120         case CHIPC_PFLASH_CFI:
  121         case CHIPC_NFLASH:
  122         case CHIPC_NFLASH_4706:
  123         case CHIPC_FLASH_NONE:
  124         default:
  125                 return (NULL);
  126         }
  127 }
  128 
  129 /**
  130  * Initialize child resource @p r with a virtual address, tag, and handle
  131  * copied from @p parent, adjusted to contain only the range defined by
  132  * @p offsize and @p size.
  133  * 
  134  * @param r The register to be initialized.
  135  * @param parent The parent bus resource that fully contains the subregion.
  136  * @param offset The subregion offset within @p parent.
  137  * @param size The subregion size.
  138  */
  139 int
  140 chipc_init_child_resource(struct resource *r,
  141     struct resource *parent, bhnd_size_t offset, bhnd_size_t size)
  142 {
  143         bus_space_handle_t      bh, child_bh;
  144         bus_space_tag_t         bt;
  145         uintptr_t               vaddr;
  146         int                     error;
  147 
  148         /* Fetch the parent resource's bus values */
  149         vaddr = (uintptr_t) rman_get_virtual(parent);
  150         bt = rman_get_bustag(parent);
  151         bh = rman_get_bushandle(parent);
  152 
  153         /* Configure child resource with offset-adjusted values */
  154         vaddr += offset;
  155         error = bus_space_subregion(bt, bh, offset, size, &child_bh);
  156         if (error)
  157                 return (error);
  158 
  159         rman_set_virtual(r, (void *) vaddr);
  160         rman_set_bustag(r, bt);
  161         rman_set_bushandle(r, child_bh);
  162 
  163         return (0);
  164 }
  165 
  166 /**
  167  * Map an interrupt line to an IRQ, and then register a corresponding SYS_RES_IRQ
  168  * with @p child's resource list.
  169  *
  170  * @param sc chipc driver state.
  171  * @param child The device to set the resource on.
  172  * @param rid The resource ID.
  173  * @param intr The interrupt line to be mapped.
  174  * @param count The length of the resource.
  175  * @param port The mapping port number (ignored if not SYS_RES_MEMORY).
  176  * @param region The mapping region number (ignored if not SYS_RES_MEMORY).
  177  */
  178 int
  179 chipc_set_irq_resource(struct chipc_softc *sc, device_t child, int rid,
  180     u_int intr)
  181 {
  182         struct chipc_devinfo    *dinfo;
  183         int                      error;
  184 
  185         KASSERT(device_get_parent(child) == sc->dev, ("not a direct child"));
  186         dinfo = device_get_ivars(child);
  187 
  188         /* We currently only support a single IRQ mapping */
  189         if (dinfo->irq_mapped) {
  190                 device_printf(sc->dev, "irq already mapped for child\n");
  191                 return (ENOMEM);
  192         }
  193 
  194         /* Map the IRQ */
  195         if ((error = bhnd_map_intr(sc->dev, intr, &dinfo->irq))) {
  196                 device_printf(sc->dev, "failed to map intr %u: %d\n", intr,
  197                     error);
  198                 return (error);
  199         }
  200 
  201         dinfo->irq_mapped = true;
  202 
  203         /* Add to child's resource list */
  204         error = bus_set_resource(child, SYS_RES_IRQ, rid, dinfo->irq, 1);
  205         if (error) {
  206                 device_printf(sc->dev, "failed to set child irq resource %d to "
  207                     "%ju: %d\n", rid, dinfo->irq, error);
  208 
  209                 bhnd_unmap_intr(sc->dev, dinfo->irq);
  210                 return (error);
  211         }
  212 
  213         return (0);
  214 }
  215 
  216 /**
  217  * Add a SYS_RES_MEMORY resource with a given resource ID, relative to the
  218  * given port and region, to @p child's resource list.
  219  * 
  220  * The specified @p region's address and size will be fetched from the bhnd(4)
  221  * bus, and bus_set_resource() will be called with @p start added the region's
  222  * actual base address.
  223  * 
  224  * To use the default region values for @p start and @p count, specify
  225  * a @p start value of 0ul, and an end value of RMAN_MAX_END
  226  * 
  227  * @param sc chipc driver state.
  228  * @param child The device to set the resource on.
  229  * @param rid The resource ID.
  230  * @param start The resource start address (if SYS_RES_MEMORY, this is
  231  * relative to @p region's base address).
  232  * @param count The length of the resource.
  233  * @param port The mapping port number (ignored if not SYS_RES_MEMORY).
  234  * @param region The mapping region number (ignored if not SYS_RES_MEMORY).
  235  */
  236 int
  237 chipc_set_mem_resource(struct chipc_softc *sc, device_t child, int rid,
  238     rman_res_t start, rman_res_t count, u_int port, u_int region)
  239 {
  240         bhnd_addr_t     region_addr;
  241         bhnd_size_t     region_size;
  242         bool            isdefault;
  243         int             error;
  244 
  245         KASSERT(device_get_parent(child) == sc->dev, ("not a direct child"));
  246         isdefault = RMAN_IS_DEFAULT_RANGE(start, count);
  247 
  248         /* Fetch region address and size */
  249         error = bhnd_get_region_addr(sc->dev, BHND_PORT_DEVICE, port,
  250             region, &region_addr, &region_size);
  251         if (error) {
  252                 device_printf(sc->dev,
  253                     "lookup of %s%u.%u failed: %d\n",
  254                     bhnd_port_type_name(BHND_PORT_DEVICE), port, region, error);
  255                 return (error);
  256         }
  257 
  258         /* Populate defaults */
  259         if (isdefault) {
  260                 start = 0;
  261                 count = region_size;
  262         }
  263 
  264         /* Verify requested range is mappable */
  265         if (start > region_size || region_size - start < count) {
  266                 device_printf(sc->dev,
  267                     "%s%u.%u region cannot map requested range %#jx+%#jx\n",
  268                     bhnd_port_type_name(BHND_PORT_DEVICE), port, region, start,
  269                     count);
  270                 return (ERANGE);
  271         }
  272 
  273         return (bus_set_resource(child, SYS_RES_MEMORY, rid,
  274             region_addr + start, count));
  275 }
  276 
  277 /*
  278  * Print a capability structure.
  279  */
  280 void
  281 chipc_print_caps(device_t dev, struct chipc_caps *caps)
  282 {
  283 #define CC_TFS(_flag) (caps->_flag ? "yes" : "no")
  284 
  285         device_printf(dev, "MIPSEB:  %-3s   | BP64:  %s\n",
  286             CC_TFS(mipseb), CC_TFS(backplane_64));
  287         device_printf(dev, "UARTs:   %-3hhu   | UGPIO: %s\n",
  288             caps->num_uarts, CC_TFS(uart_gpio));
  289         // XXX: hitting a kvprintf bug with '%#02x' not prefixing '0x' in
  290         // some cases, and not apply the field width in others
  291         device_printf(dev, "UARTClk: 0x%02x  | Flash: %u\n",
  292             caps->uart_clock, caps->flash_type);
  293         device_printf(dev, "SPROM:   %-3s   | OTP:   %s\n",
  294             CC_TFS(sprom), CC_TFS(otp_size));
  295         device_printf(dev, "CFIsz:   0x%02x  | OTPsz: 0x%02x\n",
  296             caps->cfi_width, caps->otp_size);
  297         device_printf(dev, "ExtBus:  0x%02x  | PwrCtrl: %s\n",
  298             caps->extbus_type, CC_TFS(pwr_ctrl));
  299         device_printf(dev, "PLL:     0x%02x  | JTAGM: %s\n",
  300             caps->pll_type, CC_TFS(jtag_master));
  301         device_printf(dev, "PMU:     %-3s   | ECI:   %s\n",
  302             CC_TFS(pmu), CC_TFS(eci));
  303         device_printf(dev, "SECI:    %-3s   | GSIO:  %s\n",
  304             CC_TFS(seci), CC_TFS(gsio));
  305         device_printf(dev, "AOB:     %-3s   | BootROM: %s\n",
  306             CC_TFS(aob), CC_TFS(boot_rom));
  307 
  308 #undef CC_TFS
  309 }
  310 
  311 /**
  312  * Allocate and initialize new region record.
  313  * 
  314  * @param sc Driver instance state.
  315  * @param type The port type to query.
  316  * @param port The port number to query.
  317  * @param region The region number to query.
  318  */
  319 struct chipc_region *
  320 chipc_alloc_region(struct chipc_softc *sc, bhnd_port_type type,
  321     u_int port, u_int region)
  322 {
  323         struct chipc_region     *cr;
  324         int                      error;
  325 
  326         /* Don't bother allocating a chipc_region if init will fail */
  327         if (!bhnd_is_region_valid(sc->dev, type, port, region))
  328                 return (NULL);
  329 
  330         /* Allocate and initialize region info */
  331         cr = malloc(sizeof(*cr), M_BHND, M_NOWAIT);
  332         if (cr == NULL)
  333                 return (NULL);
  334 
  335         cr->cr_port_type = type;
  336         cr->cr_port_num = port;
  337         cr->cr_region_num = region;
  338         cr->cr_res = NULL;
  339         cr->cr_refs = 0;
  340         cr->cr_act_refs = 0;
  341 
  342         error = bhnd_get_region_addr(sc->dev, type, port, region, &cr->cr_addr,
  343             &cr->cr_count);
  344         if (error) {
  345                 device_printf(sc->dev,
  346                     "fetching chipc region address failed: %d\n", error);
  347                 goto failed;
  348         }
  349 
  350         cr->cr_end = cr->cr_addr + cr->cr_count - 1;
  351 
  352         /* Fetch default resource ID for this region. Not all regions have an
  353          * assigned rid, in which case this will return -1 */
  354         cr->cr_rid = bhnd_get_port_rid(sc->dev, type, port, region);
  355 
  356         return (cr);
  357 
  358 failed:
  359         device_printf(sc->dev, "chipc region alloc failed for %s%u.%u\n",
  360             bhnd_port_type_name(type), port, region);
  361         free(cr, M_BHND);
  362         return (NULL);
  363 }
  364 
  365 /**
  366  * Deallocate the given region record and its associated resource, if any.
  367  *
  368  * @param sc Driver instance state.
  369  * @param cr Region record to be deallocated.
  370  */
  371 void
  372 chipc_free_region(struct chipc_softc *sc, struct chipc_region *cr)
  373 {
  374         KASSERT(cr->cr_refs == 0,
  375             ("chipc %s%u.%u region has %u active references",
  376              bhnd_port_type_name(cr->cr_port_type), cr->cr_port_num,
  377              cr->cr_region_num, cr->cr_refs));
  378 
  379         if (cr->cr_res != NULL) {
  380                 bhnd_release_resource(sc->dev, SYS_RES_MEMORY, cr->cr_res_rid,
  381                     cr->cr_res);
  382         }
  383 
  384         free(cr, M_BHND);
  385 }
  386 
  387 /**
  388  * Locate the region mapping the given range, if any. Returns NULL if no
  389  * valid region is found.
  390  * 
  391  * @param sc Driver instance state.
  392  * @param start start of address range.
  393  * @param end end of address range.
  394  */
  395 struct chipc_region *
  396 chipc_find_region(struct chipc_softc *sc, rman_res_t start, rman_res_t end)
  397 {
  398         struct chipc_region *cr;
  399 
  400         if (start > end)
  401                 return (NULL);
  402 
  403         STAILQ_FOREACH(cr, &sc->mem_regions, cr_link) {
  404                 if (start < cr->cr_addr || end > cr->cr_end)
  405                         continue;
  406 
  407                 /* Found */
  408                 return (cr);
  409         }
  410 
  411         /* Not found */
  412         return (NULL);
  413 }
  414 
  415 /**
  416  * Locate a region mapping by its bhnd-assigned resource id (as returned by
  417  * bhnd_get_port_rid).
  418  * 
  419  * @param sc Driver instance state.
  420  * @param rid Resource ID to query for.
  421  */
  422 struct chipc_region *
  423 chipc_find_region_by_rid(struct chipc_softc *sc, int rid)
  424 {
  425         struct chipc_region     *cr;
  426         int                      port_rid;
  427 
  428         STAILQ_FOREACH(cr, &sc->mem_regions, cr_link) {
  429                 port_rid = bhnd_get_port_rid(sc->dev, cr->cr_port_type,
  430                     cr->cr_port_num, cr->cr_region_num);
  431                 if (port_rid == -1 || port_rid != rid)
  432                         continue;
  433 
  434                 /* Found */
  435                 return (cr);
  436         }
  437 
  438         /* Not found */
  439         return (NULL);
  440 }
  441 
  442 /**
  443  * Retain a reference to a chipc_region, allocating and activating the
  444  * backing resource as required.
  445  * 
  446  * @param sc chipc driver instance state
  447  * @param cr region to retain.
  448  * @param flags specify RF_ALLOCATED to retain an allocation reference,
  449  * RF_ACTIVE to retain an activation reference.
  450  */
  451 int
  452 chipc_retain_region(struct chipc_softc *sc, struct chipc_region *cr, int flags)
  453 {
  454         int error;
  455 
  456         KASSERT(!(flags &~ (RF_ACTIVE|RF_ALLOCATED)), ("unsupported flags"));
  457 
  458         CHIPC_LOCK(sc);
  459 
  460         /* Handle allocation */
  461         if (flags & RF_ALLOCATED) {
  462                 /* If this is the first reference, allocate the resource */
  463                 if (cr->cr_refs == 0) {
  464                         KASSERT(cr->cr_res == NULL,
  465                             ("non-NULL resource has refcount"));
  466 
  467                         /* Fetch initial resource ID */                 
  468                         if ((cr->cr_res_rid = cr->cr_rid) == -1) {
  469                                 CHIPC_UNLOCK(sc);
  470                                 return (EINVAL);
  471                         }
  472 
  473                         /* Allocate resource */
  474                         cr->cr_res = bhnd_alloc_resource(sc->dev,
  475                             SYS_RES_MEMORY, &cr->cr_res_rid, cr->cr_addr,
  476                             cr->cr_end, cr->cr_count, RF_SHAREABLE);
  477                         if (cr->cr_res == NULL) {
  478                                 CHIPC_UNLOCK(sc);
  479                                 return (ENXIO);
  480                         }
  481                 }
  482                 
  483                 /* Increment allocation refcount */
  484                 cr->cr_refs++;
  485         }
  486 
  487         /* Handle activation */
  488         if (flags & RF_ACTIVE) {
  489                 KASSERT(cr->cr_refs > 0,
  490                     ("cannot activate unallocated resource"));
  491 
  492                 /* If this is the first reference, activate the resource */
  493                 if (cr->cr_act_refs == 0) {
  494                         error = bhnd_activate_resource(sc->dev, SYS_RES_MEMORY,
  495                             cr->cr_res_rid, cr->cr_res);
  496                         if (error) {
  497                                 /* Drop any allocation reference acquired
  498                                  * above */
  499                                 CHIPC_UNLOCK(sc);
  500                                 chipc_release_region(sc, cr,
  501                                     flags &~ RF_ACTIVE);
  502                                 return (error);
  503                         }
  504                 }
  505 
  506                 /* Increment activation refcount */
  507                 cr->cr_act_refs++;
  508         }
  509 
  510         CHIPC_UNLOCK(sc);
  511         return (0);
  512 }
  513 
  514 /**
  515  * Release a reference to a chipc_region, deactivating and releasing the
  516  * backing resource if the reference count hits zero.
  517  * 
  518  * @param sc chipc driver instance state
  519  * @param cr region to retain.
  520  * @param flags specify RF_ALLOCATED to release an allocation reference,
  521  * RF_ACTIVE to release an activation reference.
  522  */
  523 int
  524 chipc_release_region(struct chipc_softc *sc, struct chipc_region *cr,
  525     int flags)
  526 {
  527         int     error;
  528 
  529         CHIPC_LOCK(sc);
  530         error = 0;
  531 
  532         KASSERT(cr->cr_res != NULL, ("release on NULL region resource"));
  533 
  534         if (flags & RF_ACTIVE) {
  535                 KASSERT(cr->cr_act_refs > 0, ("RF_ACTIVE over-released"));
  536                 KASSERT(cr->cr_act_refs <= cr->cr_refs,
  537                      ("RF_ALLOCATED released with RF_ACTIVE held"));
  538 
  539                 /* If this is the last reference, deactivate the resource */
  540                 if (cr->cr_act_refs == 1) {
  541                         error = bhnd_deactivate_resource(sc->dev,
  542                             SYS_RES_MEMORY, cr->cr_res_rid, cr->cr_res);
  543                         if (error)
  544                                 goto done;
  545                 }
  546 
  547                 /* Drop our activation refcount */
  548                 cr->cr_act_refs--;
  549         }
  550 
  551         if (flags & RF_ALLOCATED) {
  552                 KASSERT(cr->cr_refs > 0, ("overrelease of refs"));
  553                 /* If this is the last reference, release the resource */
  554                 if (cr->cr_refs == 1) {
  555                         error = bhnd_release_resource(sc->dev, SYS_RES_MEMORY,
  556                             cr->cr_res_rid, cr->cr_res);
  557                         if (error)
  558                                 goto done;
  559 
  560                         cr->cr_res = NULL;
  561                 }
  562 
  563                 /* Drop our allocation refcount */
  564                 cr->cr_refs--;
  565         }
  566 
  567 done:
  568         CHIPC_UNLOCK(sc);
  569         return (error);
  570 }

Cache object: 054bae5a28d744d4484e80e58780e431


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