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/powerpc/mpc85xx/lbc.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) 2006-2008, Juniper Networks, Inc.
    3  * Copyright (c) 2008 Semihalf, Rafal Czubak
    4  * All rights reserved.
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  * 3. The name of the author may not be used to endorse or promote products
   15  *    derived from this software without specific prior written permission.
   16  *
   17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   22  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
   24  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   25  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   27  * SUCH DAMAGE.
   28  */
   29 
   30 #include <sys/cdefs.h>
   31 __FBSDID("$FreeBSD$");
   32 
   33 #include <sys/param.h>
   34 #include <sys/systm.h>
   35 #include <sys/ktr.h>
   36 #include <sys/kernel.h>
   37 #include <sys/malloc.h>
   38 #include <sys/module.h>
   39 #include <sys/bus.h>
   40 #include <sys/rman.h>
   41 #include <machine/bus.h>
   42 #include <machine/ocpbus.h>
   43 
   44 #include <vm/vm.h>
   45 #include <vm/pmap.h>
   46 
   47 #include <powerpc/mpc85xx/lbc.h>
   48 #include <powerpc/mpc85xx/mpc85xx.h>
   49 #include <powerpc/mpc85xx/ocpbus.h>
   50 
   51 struct lbc_softc {
   52         device_t                sc_dev;
   53 
   54         struct resource         *sc_res;
   55         bus_space_handle_t      sc_bsh;
   56         bus_space_tag_t         sc_bst;
   57         int                     sc_rid;
   58 
   59         struct rman             sc_rman;
   60         vm_offset_t             sc_kva[LBC_DEV_MAX];
   61 };
   62 
   63 struct lbc_devinfo {
   64         int             lbc_devtype;
   65         /* LBC child unit. It also represents resource table entry number */
   66         int             lbc_unit;
   67 };
   68 
   69 /* Resources for MPC8555CDS system */
   70 const struct lbc_resource mpc85xx_lbc_resources[] = {
   71         /* Boot flash bank */
   72         {
   73                 LBC_DEVTYPE_CFI, 0, 0xff800000, 0x00800000, 16,
   74                 LBCRES_MSEL_GPCM, LBCRES_DECC_DISABLED,
   75                 LBCRES_ATOM_DISABLED, 0
   76         },
   77 
   78         /* Second flash bank */
   79         {
   80                 LBC_DEVTYPE_CFI, 1, 0xff000000, 0x00800000, 16,
   81                 LBCRES_MSEL_GPCM, LBCRES_DECC_DISABLED,
   82                 LBCRES_ATOM_DISABLED, 0
   83         },
   84 
   85         /* DS1553 RTC/NVRAM */
   86         {
   87                 LBC_DEVTYPE_RTC, 2, 0xf8000000, 0x8000, 8,
   88                 LBCRES_MSEL_GPCM, LBCRES_DECC_DISABLED,
   89                 LBCRES_ATOM_DISABLED, 0
   90         },
   91 
   92         {0}
   93 };
   94 
   95 static int lbc_probe(device_t);
   96 static int lbc_attach(device_t);
   97 static int lbc_shutdown(device_t);
   98 static int lbc_get_resource(device_t, device_t, int, int, u_long *,
   99     u_long *);
  100 static struct resource *lbc_alloc_resource(device_t, device_t, int, int *,
  101     u_long, u_long, u_long, u_int);
  102 static int lbc_print_child(device_t, device_t);
  103 static int lbc_release_resource(device_t, device_t, int, int,
  104     struct resource *);
  105 static int lbc_read_ivar(device_t, device_t, int, uintptr_t *);
  106 
  107 /*
  108  * Bus interface definition
  109  */
  110 static device_method_t lbc_methods[] = {
  111         /* Device interface */
  112         DEVMETHOD(device_probe,         lbc_probe),
  113         DEVMETHOD(device_attach,        lbc_attach),
  114         DEVMETHOD(device_shutdown,      lbc_shutdown),
  115 
  116         /* Bus interface */
  117         DEVMETHOD(bus_print_child,      lbc_print_child),
  118         DEVMETHOD(bus_read_ivar,        lbc_read_ivar),
  119         DEVMETHOD(bus_setup_intr,       bus_generic_setup_intr),
  120         DEVMETHOD(bus_teardown_intr,    NULL),
  121 
  122         DEVMETHOD(bus_get_resource,     NULL),
  123         DEVMETHOD(bus_alloc_resource,   lbc_alloc_resource),
  124         DEVMETHOD(bus_release_resource, lbc_release_resource),
  125         DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
  126         DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
  127 
  128         { 0, 0 }
  129 };
  130 
  131 static driver_t lbc_driver = {
  132         "lbc",
  133         lbc_methods,
  134         sizeof(struct lbc_softc)
  135 };
  136 devclass_t lbc_devclass;
  137 DRIVER_MODULE(lbc, ocpbus, lbc_driver, lbc_devclass, 0, 0);
  138 
  139 static __inline void
  140 lbc_write_reg(struct lbc_softc *sc, bus_size_t off, uint32_t val)
  141 {
  142 
  143         bus_space_write_4(sc->sc_bst, sc->sc_bsh, off, val);
  144 }
  145 
  146 static __inline uint32_t
  147 lbc_read_reg(struct lbc_softc *sc, bus_size_t off)
  148 {
  149 
  150         return (bus_space_read_4(sc->sc_bst, sc->sc_bsh, off));
  151 }
  152 
  153 /*
  154  * Calculate address mask used by OR(n) registers. Use memory region size to
  155  * determine mask value. The size must be a power of two and within the range
  156  * of 32KB - 4GB. Otherwise error code is returned. Value representing
  157  * 4GB size can be passed as 0xffffffff.
  158  */
  159 static uint32_t
  160 lbc_address_mask(uint32_t size)
  161 {
  162         int n = 15;
  163 
  164         if (size == ~0UL)
  165                 return (0);
  166 
  167         while (n < 32) {
  168                 if (size == (1UL << n))
  169                         break;
  170                 n++;
  171         }
  172 
  173         if (n == 32)
  174                 return (EINVAL);
  175 
  176         return (0xffff8000 << (n - 15));
  177 }
  178 
  179 static device_t
  180 lbc_mk_child(device_t dev, const struct lbc_resource *lbcres)
  181 {
  182         struct lbc_devinfo *dinfo;
  183         device_t child;
  184 
  185         if (lbcres->lbr_unit > LBC_DEV_MAX - 1)
  186                 return (NULL);
  187 
  188         child = device_add_child(dev, NULL, -1);
  189         if (child == NULL) {
  190                 device_printf(dev, "could not add LBC child device\n");
  191                 return (NULL);
  192         }
  193         dinfo = malloc(sizeof(struct lbc_devinfo), M_DEVBUF, M_WAITOK | M_ZERO);
  194         dinfo->lbc_devtype = lbcres->lbr_devtype;
  195         dinfo->lbc_unit = lbcres->lbr_unit;
  196         device_set_ivars(child, dinfo);
  197         return (child);
  198 }
  199 
  200 static int
  201 lbc_init_child(device_t dev, device_t child)
  202 {
  203         struct lbc_softc *sc;
  204         struct lbc_devinfo *dinfo;
  205         const struct lbc_resource *res;
  206         u_long start, size;
  207         uint32_t regbuff;
  208         int error, unit;
  209 
  210         sc = device_get_softc(dev);
  211         dinfo = device_get_ivars(child);
  212 
  213         res = mpc85xx_lbc_resources;
  214 
  215         regbuff = 0;
  216         unit = -1;
  217         for (; res->lbr_devtype; res++) {
  218                 if (res->lbr_unit != dinfo->lbc_unit)
  219                         continue;
  220 
  221                 start = res->lbr_base_addr;
  222                 size = res->lbr_size;
  223                 unit = res->lbr_unit;
  224 
  225                 /*
  226                  * Configure LAW for this LBC device and map its physical
  227                  * memory region into KVA
  228                  */
  229                 error = law_enable(OCP85XX_TGTIF_LBC, start, size);
  230                 if (error)
  231                         return (error);
  232 
  233                 sc->sc_kva[unit] = (vm_offset_t)pmap_mapdev(start, size);
  234                 if (sc->sc_kva[unit] == 0) {
  235                         law_disable(OCP85XX_TGTIF_LBC, start, size);
  236                         return (ENOSPC);
  237                 }
  238 
  239                 /*
  240                  * Compute and program BR value
  241                  */
  242                 regbuff |= start;
  243 
  244                 switch (res->lbr_port_size) {
  245                 case 8:
  246                         regbuff |= (1 << 11);
  247                         break;
  248                 case 16:
  249                         regbuff |= (2 << 11);
  250                         break;
  251                 case 32:
  252                         regbuff |= (3 << 11);
  253                         break;
  254                 default:
  255                         error = EINVAL;
  256                         goto fail;
  257                 }
  258                 regbuff |= (res->lbr_decc << 9);
  259                 regbuff |= (res->lbr_wp << 8);
  260                 regbuff |= (res->lbr_msel << 5);
  261                 regbuff |= (res->lbr_atom << 2);
  262                 regbuff |= 1;
  263 
  264                 lbc_write_reg(sc, LBC85XX_BR(unit), regbuff);
  265 
  266                 /*
  267                  * Compute and program OR value
  268                  */
  269                 regbuff = 0;
  270                 regbuff |= lbc_address_mask(size);
  271 
  272                 switch (res->lbr_msel) {
  273                 case LBCRES_MSEL_GPCM:
  274                         /* TODO Add flag support for option registers */
  275                         regbuff |= 0x00000ff7;
  276                         break;
  277                 case LBCRES_MSEL_FCM:
  278                         printf("FCM mode not supported yet!");
  279                         error = ENOSYS;
  280                         goto fail;
  281                 case LBCRES_MSEL_UPMA:
  282                 case LBCRES_MSEL_UPMB:
  283                 case LBCRES_MSEL_UPMC:
  284                         printf("UPM mode not supported yet!");
  285                         error = ENOSYS;
  286                         goto fail;
  287                 }
  288 
  289                 lbc_write_reg(sc, LBC85XX_OR(unit), regbuff);
  290 
  291                 return (0);
  292         }
  293 fail:
  294         if (unit != -1) {
  295                 law_disable(OCP85XX_TGTIF_LBC, start, size);
  296                 pmap_unmapdev(sc->sc_kva[unit], size);
  297                 return (error);
  298         } else
  299                 return (ENOENT);
  300 }
  301 
  302 static int
  303 lbc_probe(device_t dev)
  304 {
  305         device_t parent;
  306         uintptr_t devtype;
  307         int error;
  308 
  309         parent = device_get_parent(dev);
  310         error = BUS_READ_IVAR(parent, dev, OCPBUS_IVAR_DEVTYPE, &devtype);
  311         if (error)
  312                 return (error);
  313         if (devtype != OCPBUS_DEVTYPE_LBC)
  314                 return (ENXIO);
  315 
  316         device_set_desc(dev, "Freescale MPC85xx Local Bus Controller");
  317         return (BUS_PROBE_DEFAULT);
  318 }
  319 
  320 static int
  321 lbc_attach(device_t dev)
  322 {
  323         struct lbc_softc *sc;
  324         struct rman *rm;
  325         const struct lbc_resource *lbcres;
  326         int error;
  327 
  328         sc = device_get_softc(dev);
  329         sc->sc_dev = dev;
  330 
  331         sc->sc_rid = 0;
  332         sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid,
  333             RF_ACTIVE);
  334         if (sc->sc_res == NULL)
  335                 return (ENXIO);
  336 
  337         sc->sc_bst = rman_get_bustag(sc->sc_res);
  338         sc->sc_bsh = rman_get_bushandle(sc->sc_res);
  339 
  340         rm = &sc->sc_rman;
  341         rm->rm_type = RMAN_ARRAY;
  342         rm->rm_descr = "MPC85XX Local Bus Space";
  343         rm->rm_start = 0UL;
  344         rm->rm_end = ~0UL;
  345         error = rman_init(rm);
  346         if (error)
  347                 goto fail;
  348 
  349         error = rman_manage_region(rm, rm->rm_start, rm->rm_end);
  350         if (error) {
  351                 rman_fini(rm);
  352                 goto fail;
  353         }
  354 
  355         /*
  356          * Initialize configuration register:
  357          * - enable Local Bus
  358          * - set data buffer control signal function
  359          * - disable parity byte select
  360          * - set ECC parity type
  361          * - set bus monitor timing and timer prescale
  362          */
  363         lbc_write_reg(sc, LBC85XX_LBCR, 0x00000000);
  364 
  365         /*
  366          * Initialize clock ratio register:
  367          * - disable PLL bypass mode
  368          * - configure LCLK delay cycles for the assertion of LALE
  369          * - set system clock divider
  370          */
  371         lbc_write_reg(sc, LBC85XX_LCRR, 0x00030008);
  372 
  373         lbcres = mpc85xx_lbc_resources;
  374 
  375         for (; lbcres->lbr_devtype; lbcres++)
  376                 if (!lbc_mk_child(dev, lbcres)) {
  377                         error = ENXIO;
  378                         goto fail;
  379                 }
  380 
  381         return (bus_generic_attach(dev));
  382 
  383 fail:
  384         bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res);
  385         return (error);
  386 }
  387 
  388 static int
  389 lbc_shutdown(device_t dev)
  390 {
  391 
  392         /* TODO */
  393         return(0);
  394 }
  395 
  396 static struct resource *
  397 lbc_alloc_resource(device_t dev, device_t child, int type, int *rid,
  398     u_long start, u_long end, u_long count, u_int flags)
  399 {
  400         struct lbc_softc *sc;
  401         struct lbc_devinfo *dinfo;
  402         struct resource *rv;
  403         struct rman *rm;
  404         int error;
  405 
  406         sc = device_get_softc(dev);
  407         dinfo = device_get_ivars(child);
  408 
  409         if (type != SYS_RES_MEMORY && type != SYS_RES_IRQ)
  410                 return (NULL);
  411 
  412         /* We only support default allocations. */
  413         if (start != 0ul || end != ~0ul)
  414                 return (NULL);
  415 
  416         if (type == SYS_RES_IRQ)
  417                 return (bus_alloc_resource(dev, type, rid, start, end, count,
  418                     flags));
  419 
  420         if (!sc->sc_kva[dinfo->lbc_unit]) {
  421                 error = lbc_init_child(dev, child);
  422                 if (error)
  423                         return (NULL);
  424         }
  425 
  426         error = lbc_get_resource(dev, child, type, *rid, &start, &count);
  427         if (error)
  428                 return (NULL);
  429 
  430         rm = &sc->sc_rman;
  431         end = start + count - 1;
  432         rv = rman_reserve_resource(rm, start, end, count, flags, child);
  433         if (rv != NULL) {
  434                 rman_set_bustag(rv, &bs_be_tag);
  435                 rman_set_bushandle(rv, rman_get_start(rv));
  436         }
  437         return (rv);
  438 }
  439 
  440 static int
  441 lbc_print_child(device_t dev, device_t child)
  442 {
  443         u_long size, start;
  444         int error, retval, rid;
  445 
  446         retval = bus_print_child_header(dev, child);
  447 
  448         rid = 0;
  449         while (1) {
  450                 error = lbc_get_resource(dev, child, SYS_RES_MEMORY, rid,
  451                     &start, &size);
  452                 if (error)
  453                         break;
  454                 retval += (rid == 0) ? printf(" iomem ") : printf(",");
  455                 retval += printf("%#lx", start);
  456                 if (size > 1)
  457                         retval += printf("-%#lx", start + size - 1);
  458                 rid++;
  459         }
  460 
  461         retval += bus_print_child_footer(dev, child);
  462         return (retval);
  463 }
  464 
  465 static int
  466 lbc_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
  467 {
  468         struct lbc_devinfo *dinfo;
  469 
  470         if (device_get_parent(child) != dev)
  471                 return (EINVAL);
  472 
  473         dinfo = device_get_ivars(child);
  474 
  475         switch (index) {
  476         case LBC_IVAR_DEVTYPE:
  477                 *result = dinfo->lbc_devtype;
  478                 return (0);
  479         default:
  480                 break;
  481         }
  482         return (EINVAL);
  483 }
  484 
  485 static int
  486 lbc_release_resource(device_t dev, device_t child, int type, int rid,
  487     struct resource *res)
  488 {
  489 
  490         return (rman_release_resource(res));
  491 }
  492 
  493 static int
  494 lbc_get_resource(device_t dev, device_t child, int type, int rid,
  495     u_long *startp, u_long *countp)
  496 {
  497         struct lbc_softc *sc;
  498         struct lbc_devinfo *dinfo;
  499         const struct lbc_resource *lbcres;
  500 
  501         if (type != SYS_RES_MEMORY)
  502                 return (ENOENT);
  503 
  504         /* Currently all LBC devices have a single RID per type. */
  505         if (rid != 0)
  506                 return (ENOENT);
  507 
  508         sc = device_get_softc(dev);
  509         dinfo = device_get_ivars(child);
  510 
  511         if ((dinfo->lbc_unit < 0) || (dinfo->lbc_unit > (LBC_DEV_MAX - 1)))
  512                 return (EINVAL);
  513 
  514         lbcres = mpc85xx_lbc_resources;
  515 
  516         switch (dinfo->lbc_devtype) {
  517         case LBC_DEVTYPE_CFI:
  518         case LBC_DEVTYPE_RTC:
  519                 for (; lbcres->lbr_devtype; lbcres++) {
  520                         if (dinfo->lbc_unit == lbcres->lbr_unit) {
  521                                 *startp = sc->sc_kva[lbcres->lbr_unit];
  522                                 *countp = lbcres->lbr_size;
  523                                 return (0);
  524                         }
  525                 }
  526         default:
  527                 return (EDOOFUS);
  528         }
  529         return (0);
  530 }

Cache object: 639a6a6106a6db7d817c559de84119fe


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