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/mips/ingenic/jz4780_nemc.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 2015 Alexander Kabaev <kan@FreeBSD.org>
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  */
   26 
   27 /*
   28  * Ingenic JZ4780 NAND and External Memory Controller (NEMC) driver.
   29  *
   30  */
   31 
   32 #include <sys/cdefs.h>
   33 __FBSDID("$FreeBSD$");
   34 
   35 #include <sys/param.h>
   36 #include <sys/systm.h>
   37 #include <sys/conf.h>
   38 #include <sys/bus.h>
   39 #include <sys/kernel.h>
   40 #include <sys/module.h>
   41 #include <sys/lock.h>
   42 #include <sys/mutex.h>
   43 #include <sys/resource.h>
   44 #include <sys/rman.h>
   45 
   46 #include <machine/bus.h>
   47 
   48 #include <dev/extres/clk/clk.h>
   49 
   50 #include <dev/fdt/fdt_common.h>
   51 #include <dev/fdt/simplebus.h>
   52 #include <dev/ofw/ofw_bus.h>
   53 #include <dev/ofw/ofw_bus_subr.h>
   54 
   55 #include <mips/ingenic/jz4780_regs.h>
   56 
   57 struct jz4780_nemc_devinfo {
   58         struct simplebus_devinfo sinfo;
   59         uint32_t                 bank;
   60 };
   61 
   62 struct jz4780_nemc_softc {
   63         struct simplebus_softc  simplebus_sc;
   64         device_t                dev;
   65         struct resource         *res[1];
   66         uint32_t                banks;
   67         uint32_t                clock_tick_psecs;
   68         clk_t                   clk;
   69 };
   70 
   71 static struct resource_spec jz4780_nemc_spec[] = {
   72         { SYS_RES_MEMORY, 0, RF_ACTIVE },
   73         { -1, 0 }
   74 };
   75 
   76 #define CSR_WRITE_4(sc, reg, val)       bus_write_4((sc)->res[0], reg, (val))
   77 #define CSR_READ_4(sc, reg)             bus_read_4((sc)->res[0], reg)
   78 
   79 static int jz4780_nemc_probe(device_t dev);
   80 static int jz4780_nemc_attach(device_t dev);
   81 static int jz4780_nemc_detach(device_t dev);
   82 
   83 static int
   84 jz4780_nemc_probe(device_t dev)
   85 {
   86 
   87         if (!ofw_bus_status_okay(dev))
   88                 return (ENXIO);
   89 
   90         if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-nemc"))
   91                 return (ENXIO);
   92 
   93         device_set_desc(dev, "Ingenic JZ4780 NEMC");
   94 
   95         return (BUS_PROBE_DEFAULT);
   96 }
   97 
   98 #define JZ4780_NEMC_NS_TO_TICKS(sc, val) howmany((val) * 1000,  (sc)->clock_tick_psecs)
   99 
  100 /* Use table from JZ4780 programmers manual to convert ticks to tBP/tAW register values */
  101 static const uint8_t ticks_to_tBP_tAW[32] = {
  102         0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,  /* 1:1 mapping */
  103         11, 11,                            /* 12 cycles */
  104         12, 12, 12,                        /* 15 cycles */
  105         13, 13, 13, 13, 13,                /* 20 cycles */
  106         14, 14, 14, 14, 14,                /* 25 cycles */
  107         15, 15, 15, 15, 15, 15             /* 31 cycles */
  108 };
  109 
  110 static int
  111 jz4780_nemc_configure_bank(struct jz4780_nemc_softc *sc,
  112         device_t dev, u_int bank)
  113 {
  114         uint32_t smcr, cycles;
  115         phandle_t node;
  116         pcell_t   val;
  117 
  118         /* Check if bank is configured already */
  119         if (sc->banks & (1 << bank))
  120                 return 0;
  121 
  122         smcr = CSR_READ_4(sc, JZ_NEMC_SMCR(bank));
  123 
  124         smcr &= ~JZ_NEMC_SMCR_SMT_MASK;
  125         smcr |= JZ_NEMC_SMCR_SMT_NORMAL << JZ_NEMC_SMCR_SMT_SHIFT;
  126 
  127         node = ofw_bus_get_node(dev);
  128         if (OF_getencprop(node, "ingenic,nemc-tAS", &val, sizeof(val)) > 0) {
  129                 cycles = JZ4780_NEMC_NS_TO_TICKS(sc, val);
  130                 if (cycles > 15) {
  131                         device_printf(sc->dev,
  132                             "invalid value of %s %u (%u cycles), maximum %u cycles supported\n",
  133                             "ingenic,nemc-tAS", val, cycles, 15);
  134                         return -1;
  135                 }
  136                 smcr &= ~JZ_NEMC_SMCR_TAS_MASK;
  137                 smcr |= cycles << JZ_NEMC_SMCR_TAS_SHIFT;
  138         }
  139 
  140         if (OF_getencprop(node, "ingenic,nemc-tAH", &val, sizeof(val)) > 0) {
  141                 cycles = JZ4780_NEMC_NS_TO_TICKS(sc, val);
  142                 if (cycles > 15) {
  143                         device_printf(sc->dev,
  144                             "invalid value of %s %u (%u cycles), maximum %u cycles supported\n",
  145                             "ingenic,nemc-tAH", val, cycles, 15);
  146                         return -1;
  147                 }
  148                 smcr &= ~JZ_NEMC_SMCR_TAH_MASK;
  149                 smcr |= cycles << JZ_NEMC_SMCR_TAH_SHIFT;
  150         }
  151 
  152         if (OF_getencprop(node, "ingenic,nemc-tBP", &val, sizeof(val)) > 0) {
  153                 cycles = JZ4780_NEMC_NS_TO_TICKS(sc, val);
  154                 if (cycles > 31) {
  155                         device_printf(sc->dev,
  156                             "invalid value of %s %u (%u cycles), maximum %u cycles supported\n",
  157                             "ingenic,nemc-tBP", val, cycles, 15);
  158                         return -1;
  159                 }
  160                 smcr &= ~JZ_NEMC_SMCR_TBP_MASK;
  161                 smcr |= ticks_to_tBP_tAW[cycles] << JZ_NEMC_SMCR_TBP_SHIFT;
  162         }
  163 
  164         if (OF_getencprop(node, "ingenic,nemc-tAW", &val, sizeof(val)) > 0) {
  165                 cycles = JZ4780_NEMC_NS_TO_TICKS(sc, val);
  166                 if (cycles > 31) {
  167                         device_printf(sc->dev,
  168                             "invalid value of %s %u (%u cycles), maximum %u cycles supported\n",
  169                             "ingenic,nemc-tAW", val, cycles, 15);
  170                         return -1;
  171                 }
  172                 smcr &= ~JZ_NEMC_SMCR_TAW_MASK;
  173                 smcr |= ticks_to_tBP_tAW[cycles] << JZ_NEMC_SMCR_TAW_SHIFT;
  174         }
  175 
  176         if (OF_getencprop(node, "ingenic,nemc-tSTRV", &val, sizeof(val)) > 0) {
  177                 cycles = JZ4780_NEMC_NS_TO_TICKS(sc, val);
  178                 if (cycles > 63) {
  179                         device_printf(sc->dev,
  180                             "invalid value of %s %u (%u cycles), maximum %u cycles supported\n",
  181                             "ingenic,nemc-tSTRV", val, cycles, 15);
  182                         return -1;
  183                 }
  184                 smcr &= ~JZ_NEMC_SMCR_STRV_MASK;
  185                 smcr |= cycles << JZ_NEMC_SMCR_STRV_SHIFT;
  186         }
  187         CSR_WRITE_4(sc, JZ_NEMC_SMCR(bank), smcr);
  188         sc->banks |= (1 << bank);
  189         return 0;
  190 }
  191 
  192 /* Wholesale copy of simplebus routine */
  193 static int
  194 jz4780_nemc_fill_ranges(phandle_t node, struct simplebus_softc *sc)
  195 {
  196         int host_address_cells;
  197         cell_t *base_ranges;
  198         ssize_t nbase_ranges;
  199         int err;
  200         int i, j, k;
  201 
  202         err = OF_searchencprop(OF_parent(node), "#address-cells",
  203             &host_address_cells, sizeof(host_address_cells));
  204         if (err <= 0)
  205                 return (-1);
  206 
  207         nbase_ranges = OF_getproplen(node, "ranges");
  208         if (nbase_ranges < 0)
  209                 return (-1);
  210         sc->nranges = nbase_ranges / sizeof(cell_t) /
  211             (sc->acells + host_address_cells + sc->scells);
  212         if (sc->nranges == 0)
  213                 return (0);
  214 
  215         sc->ranges = malloc(sc->nranges * sizeof(sc->ranges[0]),
  216             M_DEVBUF, M_WAITOK);
  217         base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK);
  218         OF_getencprop(node, "ranges", base_ranges, nbase_ranges);
  219 
  220         for (i = 0, j = 0; i < sc->nranges; i++) {
  221                 sc->ranges[i].bus = 0;
  222                 for (k = 0; k < sc->acells; k++) {
  223                         sc->ranges[i].bus <<= 32;
  224                         sc->ranges[i].bus |= base_ranges[j++];
  225                 }
  226                 sc->ranges[i].host = 0;
  227                 for (k = 0; k < host_address_cells; k++) {
  228                         sc->ranges[i].host <<= 32;
  229                         sc->ranges[i].host |= base_ranges[j++];
  230                 }
  231                 sc->ranges[i].size = 0;
  232                 for (k = 0; k < sc->scells; k++) {
  233                         sc->ranges[i].size <<= 32;
  234                         sc->ranges[i].size |= base_ranges[j++];
  235                 }
  236         }
  237 
  238         free(base_ranges, M_DEVBUF);
  239         return (sc->nranges);
  240 }
  241 
  242 static int
  243 jz4780_nemc_attach(device_t dev)
  244 {
  245         struct jz4780_nemc_softc *sc = device_get_softc(dev);
  246         phandle_t node;
  247         uint64_t freq;
  248 
  249         sc->dev = dev;
  250 
  251         if (bus_alloc_resources(dev, jz4780_nemc_spec, sc->res)) {
  252                 device_printf(dev, "could not allocate resources for device\n");
  253                 return (ENXIO);
  254         }
  255 
  256         node = ofw_bus_get_node(dev);
  257 
  258         /* Initialize simplebus and enumerate resources */
  259         simplebus_init(dev, node);
  260 
  261         if (jz4780_nemc_fill_ranges(node, &sc->simplebus_sc) < 0)
  262                 goto error;
  263 
  264         /* Figure our underlying clock rate. */
  265         if (clk_get_by_ofw_index(dev, 0, 0, &sc->clk) != 0) {
  266                 device_printf(dev, "could not lookup device clock\n");
  267                 goto error;
  268         }
  269         if (clk_enable(sc->clk) != 0) {
  270                 device_printf(dev, "could not enable device clock\n");
  271                 goto error;
  272         }
  273         if (clk_get_freq(sc->clk, &freq) != 0) {
  274                 device_printf(dev, "could not determine clock speed\n");
  275                 goto error;
  276         }
  277 
  278         /* Convert clock frequency to picoseconds-per-tick value. */
  279         sc->clock_tick_psecs = (uint32_t)(1000000000000ULL / freq);
  280 
  281         /*
  282          * Allow devices to identify.
  283          */
  284         bus_generic_probe(dev);
  285 
  286         /*
  287          * Now walk the tree and attach top level devices
  288          */
  289         for (node = OF_child(node); node > 0; node = OF_peer(node))
  290                 simplebus_add_device(dev, node, 0, NULL, -1, NULL);
  291 
  292         return (bus_generic_attach(dev));
  293 error:
  294         jz4780_nemc_detach(dev);
  295         return (ENXIO);
  296 }
  297 
  298 static int
  299 jz4780_nemc_detach(device_t dev)
  300 {
  301         struct jz4780_nemc_softc *sc = device_get_softc(dev);
  302 
  303         bus_generic_detach(dev);
  304         if (sc->clk != NULL)
  305                 clk_release(sc->clk);
  306         bus_release_resources(dev, jz4780_nemc_spec, sc->res);
  307         return (0);
  308 }
  309 
  310 static int
  311 jz4780_nemc_decode_bank(struct simplebus_softc *sc, struct resource *r,
  312     u_int *bank)
  313 {
  314         rman_res_t start, end;
  315         int i;
  316 
  317         start = rman_get_start(r);
  318         end = rman_get_end(r);
  319 
  320         /* Remap through ranges property */
  321         for (i = 0; i < sc->nranges; i++) {
  322                 if (start >= sc->ranges[i].host && end <
  323                     sc->ranges[i].host + sc->ranges[i].size) {
  324                         *bank = (sc->ranges[i].bus >> 32);
  325                         return (0);
  326                 }
  327         }
  328         return (1);
  329 }
  330 
  331 static int
  332 jz4780_nemc_activate_resource(device_t bus, device_t child, int type, int rid,
  333     struct resource *r)
  334 {
  335         struct jz4780_nemc_softc *sc;
  336         u_int bank;
  337         int err;
  338 
  339         if (type == SYS_RES_MEMORY) {
  340                 sc = device_get_softc(bus);
  341 
  342                 /* Figure out on what bank device is residing */
  343                 err = jz4780_nemc_decode_bank(&sc->simplebus_sc, r, &bank);
  344                 if (err == 0) {
  345                         /* Attempt to configure the bank if not done already */
  346                         err = jz4780_nemc_configure_bank(sc, child, bank);
  347                         if (err != 0)
  348                                 return (err);
  349                 }
  350         }
  351 
  352         /* Call default implementation to finish the work */
  353         return (bus_generic_activate_resource(bus, child,
  354                 type, rid, r));
  355 }
  356 
  357 static device_method_t jz4780_nemc_methods[] = {
  358         /* Device interface */
  359         DEVMETHOD(device_probe,         jz4780_nemc_probe),
  360         DEVMETHOD(device_attach,        jz4780_nemc_attach),
  361         DEVMETHOD(device_detach,        jz4780_nemc_detach),
  362 
  363         /* Overrides to configure bank on resource activation */
  364         DEVMETHOD(bus_activate_resource, jz4780_nemc_activate_resource),
  365 
  366         DEVMETHOD_END
  367 };
  368 
  369 static devclass_t jz4780_nemc_devclass;
  370 DEFINE_CLASS_1(nemc, jz4780_nemc_driver, jz4780_nemc_methods,
  371         sizeof(struct jz4780_nemc_softc), simplebus_driver);
  372 DRIVER_MODULE(jz4780_nemc, simplebus, jz4780_nemc_driver,
  373     jz4780_nemc_devclass, 0, 0);

Cache object: 2390b43543a0e1d3fbef94abb759e8a4


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