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/ti/clk/ti_clkctrl.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 2016 Michal Meloun <mmel@FreeBSD.org>
    5  *
    6  * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
    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  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   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  * $FreeBSD$
   30  */
   31 
   32 #include <sys/cdefs.h>
   33 __FBSDID("$FreeBSD$");
   34 
   35 #include <sys/param.h>
   36 #include <sys/systm.h>
   37 #include <sys/bus.h>
   38 #include <sys/fbio.h>
   39 #include <sys/kernel.h>
   40 #include <sys/module.h>
   41 #include <sys/rman.h>
   42 #include <sys/resource.h>
   43 #include <machine/bus.h>
   44 #include <vm/vm.h>
   45 #include <vm/vm_extern.h>
   46 #include <vm/vm_kern.h>
   47 #include <vm/pmap.h>
   48 
   49 #include <dev/fdt/simplebus.h>
   50 
   51 #include <dev/ofw/ofw_bus.h>
   52 #include <dev/ofw/ofw_bus_subr.h>
   53 
   54 #include <arm/ti/clk/ti_clk_clkctrl.h>
   55 #include <arm/ti/ti_omap4_cm.h>
   56 #include <arm/ti/ti_cpuid.h>
   57 
   58 #if 0
   59 #define DPRINTF(dev, msg...) device_printf(dev, msg)
   60 #else
   61 #define DPRINTF(dev, msg...)
   62 #endif
   63 
   64 #define L4LS_CLKCTRL_38         2
   65 #define L4_WKUP_CLKCTRL_0       1
   66 #define NO_SPECIAL_REG          0
   67 
   68 /* Documentation/devicetree/bindings/clock/ti-clkctrl.txt */
   69 
   70 #define TI_CLKCTRL_L4_WKUP      5
   71 #define TI_CLKCTRL_L4_SECURE    4
   72 #define TI_CLKCTRL_L4_PER       3
   73 #define TI_CLKCTRL_L4_CFG       2
   74 #define TI_CLKCTRL              1
   75 #define TI_CLKCTRL_END          0
   76 
   77 static struct ofw_compat_data compat_data[] = {
   78         { "ti,clkctrl-l4-wkup",         TI_CLKCTRL_L4_WKUP },
   79         { "ti,clkctrl-l4-secure",       TI_CLKCTRL_L4_SECURE },
   80         { "ti,clkctrl-l4-per",          TI_CLKCTRL_L4_PER },
   81         { "ti,clkctrl-l4-cfg",          TI_CLKCTRL_L4_CFG },
   82         { "ti,clkctrl",                 TI_CLKCTRL },
   83         { NULL,                         TI_CLKCTRL_END }
   84 };
   85 
   86 struct ti_clkctrl_softc {
   87         device_t                        dev;
   88 
   89         struct clkdom                   *clkdom;
   90 };
   91 
   92 static int ti_clkctrl_probe(device_t dev);
   93 static int ti_clkctrl_attach(device_t dev);
   94 static int ti_clkctrl_detach(device_t dev);
   95 int clkctrl_ofw_map(struct clkdom *clkdom, uint32_t ncells,
   96     phandle_t *cells, struct clknode **clk);
   97 static int
   98 create_clkctrl(struct ti_clkctrl_softc *sc, cell_t *reg, uint32_t index, uint32_t reg_offset,
   99     uint64_t parent_offset, const char *org_name, bool special_gdbclk_reg);
  100 
  101 static int
  102 ti_clkctrl_probe(device_t dev)
  103 {
  104         if (!ofw_bus_status_okay(dev))
  105                 return (ENXIO);
  106 
  107         if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
  108                 return (ENXIO);
  109 
  110         device_set_desc(dev, "TI clkctrl");
  111 
  112         return (BUS_PROBE_DEFAULT);
  113 }
  114 
  115 static int
  116 ti_clkctrl_attach(device_t dev)
  117 {
  118         struct ti_clkctrl_softc *sc;
  119         phandle_t node;
  120         cell_t  *reg;
  121         ssize_t numbytes_reg;
  122         int num_reg, err, ti_clock_cells;
  123         uint32_t index, reg_offset, reg_address;
  124         const char *org_name;
  125         uint64_t parent_offset;
  126         uint8_t special_reg = NO_SPECIAL_REG;
  127 
  128         sc = device_get_softc(dev);
  129         sc->dev = dev;
  130         node = ofw_bus_get_node(dev);
  131 
  132         /* Sanity check */
  133         err = OF_searchencprop(node, "#clock-cells",
  134                 &ti_clock_cells, sizeof(ti_clock_cells));
  135         if (err == -1) {
  136                 device_printf(sc->dev, "Failed to get #clock-cells\n");
  137                 return (ENXIO);
  138         }
  139 
  140         if (ti_clock_cells != 2) {
  141                 device_printf(sc->dev, "clock cells(%d) != 2\n",
  142                         ti_clock_cells);
  143                 return (ENXIO);
  144         }
  145 
  146         /* Grab the content of reg properties */
  147         numbytes_reg = OF_getproplen(node, "reg");
  148         if (numbytes_reg == 0) {
  149                 device_printf(sc->dev, "reg property empty - check your devicetree\n");
  150                 return (ENXIO);
  151         }
  152         num_reg = numbytes_reg / sizeof(cell_t);
  153 
  154         reg = malloc(numbytes_reg, M_DEVBUF, M_WAITOK);
  155         OF_getencprop(node, "reg", reg, numbytes_reg);
  156 
  157         /* Create clock domain */
  158         sc->clkdom = clkdom_create(sc->dev);
  159         if (sc->clkdom == NULL) {
  160                 free(reg, M_DEVBUF);
  161                 DPRINTF(sc->dev, "Failed to create clkdom\n");
  162                 return (ENXIO);
  163         }
  164         clkdom_set_ofw_mapper(sc->clkdom, clkctrl_ofw_map);
  165 
  166         /* Create clock nodes */
  167         /* name */
  168         clk_parse_ofw_clk_name(sc->dev, node, &org_name);
  169 
  170         /* Get parent range */
  171         parent_offset = ti_omap4_cm_get_simplebus_base_host(device_get_parent(dev));
  172 
  173         /* Check if this is a clkctrl with special registers like gpio */
  174         switch (ti_chip()) {
  175 #ifdef SOC_OMAP4
  176         case CHIP_OMAP_4:
  177                 /* FIXME: Todo */
  178                 break;
  179 
  180 #endif /* SOC_OMAP4 */
  181 #ifdef SOC_TI_AM335X
  182         /* Checkout TRM 8.1.12.1.29 - 8.1.12.31 and 8.1.12.2.3
  183          * and the DTS.
  184          */
  185         case CHIP_AM335X:
  186                 if (strcmp(org_name, "l4ls-clkctrl@38") == 0)
  187                         special_reg = L4LS_CLKCTRL_38;
  188                 else if (strcmp(org_name, "l4-wkup-clkctrl@0") == 0)
  189                         special_reg = L4_WKUP_CLKCTRL_0;
  190                 break;
  191 #endif /* SOC_TI_AM335X */
  192         default:
  193                 break;
  194         }
  195 
  196         /* reg property has a pair of (base address, length) */
  197         for (index = 0; index < num_reg; index += 2) {
  198                 for (reg_offset = 0; reg_offset < reg[index+1]; reg_offset += sizeof(cell_t)) {
  199                         err = create_clkctrl(sc, reg, index, reg_offset, parent_offset,
  200                             org_name, false);
  201                         if (err)
  202                                 goto cleanup;
  203 
  204                         /* Create special clkctrl for GDBCLK in GPIO registers */
  205                         switch (special_reg) {
  206                         case NO_SPECIAL_REG:
  207                                 break;
  208                         case L4LS_CLKCTRL_38:
  209                                 reg_address = reg[index] + reg_offset-reg[0];
  210                                 if (reg_address == 0x74 ||
  211                                     reg_address == 0x78 ||
  212                                     reg_address == 0x7C)
  213                                 {
  214                                         err = create_clkctrl(sc, reg, index, reg_offset,
  215                                             parent_offset, org_name, true);
  216                                         if (err)
  217                                                 goto cleanup;
  218                                 }
  219                                 break;
  220                         case L4_WKUP_CLKCTRL_0:
  221                                 reg_address = reg[index] + reg_offset - reg[0];
  222                                 if (reg_address == 0x8)
  223                                 {
  224                                         err = create_clkctrl(sc, reg, index, reg_offset,
  225                                             parent_offset, org_name, true);
  226                                         if (err)
  227                                                 goto cleanup;
  228                                 }
  229                                 break;
  230                         } /* switch (special_reg) */
  231                 } /* inner for */
  232         } /* for */
  233 
  234         err = clkdom_finit(sc->clkdom);
  235         if (err) {
  236                 DPRINTF(sc->dev, "Clk domain finit fails %x.\n", err);
  237                 err = ENXIO;
  238                 goto cleanup;
  239         }
  240 
  241 cleanup:
  242         OF_prop_free(__DECONST(char *, org_name));
  243 
  244         free(reg, M_DEVBUF);
  245 
  246         if (err)
  247                 return (err);
  248 
  249         return (bus_generic_attach(dev));
  250 }
  251 
  252 static int
  253 ti_clkctrl_detach(device_t dev)
  254 {
  255         return (EBUSY);
  256 }
  257 
  258 /* modified version of default mapper from clk.c */
  259 int
  260 clkctrl_ofw_map(struct clkdom *clkdom, uint32_t ncells,
  261     phandle_t *cells, struct clknode **clk) {
  262         if (ncells == 0)
  263                 *clk = clknode_find_by_id(clkdom, 1);
  264         else if (ncells == 1)
  265                 *clk = clknode_find_by_id(clkdom, cells[0]);
  266         else if (ncells == 2) {
  267                 /* To avoid collision with other IDs just add one.
  268                  * All other registers has an offset of 4 from each other.
  269                  */
  270                 if (cells[1])
  271                         *clk = clknode_find_by_id(clkdom, cells[0]+1);
  272                 else
  273                         *clk = clknode_find_by_id(clkdom, cells[0]);
  274         }
  275         else
  276                 return (ERANGE);
  277 
  278         if (*clk == NULL)
  279                 return (ENXIO);
  280 
  281         return (0);
  282 }
  283 
  284 static int
  285 create_clkctrl(struct ti_clkctrl_softc *sc, cell_t *reg, uint32_t index, uint32_t reg_offset,
  286     uint64_t parent_offset, const char *org_name, bool special_gdbclk_reg) {
  287         struct ti_clk_clkctrl_def def;
  288         char *name;
  289         size_t name_len;
  290         int err;
  291 
  292         name_len = strlen(org_name) + 1 + 5; /* 5 = _xxxx */
  293         name = malloc(name_len, M_OFWPROP, M_WAITOK);
  294 
  295         /*
  296          * Check out XX_CLKCTRL-INDEX(offset)-macro dance in
  297          * sys/gnu/dts/dts/include/dt-bindings/clock/am3.h
  298          * sys/gnu/dts/dts/include/dt-bindings/clock/am4.h
  299          * sys/gnu/dts/dts/include/dt-bindings/clock/dra7.h
  300          * reg[0] are in practice the same as the offset described in the dts.
  301          */
  302         /* special_gdbclk_reg are 0 or 1 */
  303         def.clkdef.id = reg[index] + reg_offset - reg[0] + special_gdbclk_reg;
  304         def.register_offset = parent_offset + reg[index] + reg_offset;
  305 
  306         /* Indicate this clkctrl is special and dont use IDLEST/MODULEMODE */
  307         def.gdbclk = special_gdbclk_reg;
  308 
  309         /* Make up an uniq name in the namespace for each clkctrl */
  310         snprintf(name, name_len, "%s_%x",
  311                 org_name, def.clkdef.id);
  312         def.clkdef.name = (const char *) name;
  313 
  314         DPRINTF(sc->dev, "ti_clkctrl_attach: reg[%d]: %s %x\n",
  315                 index, def.clkdef.name, def.clkdef.id);
  316 
  317         /* No parent name */
  318         def.clkdef.parent_cnt = 0;
  319 
  320         /* set flags */
  321         def.clkdef.flags = 0x0;
  322 
  323         /* Register the clkctrl */
  324         err = ti_clknode_clkctrl_register(sc->clkdom, &def);
  325         if (err) {
  326                 DPRINTF(sc->dev,
  327                         "ti_clknode_clkctrl_register[%d:%d] failed %x\n",
  328                         index, reg_offset, err);
  329                 err = ENXIO;
  330         }
  331         OF_prop_free(name);
  332         return (err);
  333 }
  334 
  335 static device_method_t ti_clkctrl_methods[] = {
  336         /* Device interface */
  337         DEVMETHOD(device_probe,         ti_clkctrl_probe),
  338         DEVMETHOD(device_attach,        ti_clkctrl_attach),
  339         DEVMETHOD(device_detach,        ti_clkctrl_detach),
  340 
  341         DEVMETHOD_END
  342 };
  343 
  344 DEFINE_CLASS_0(ti_clkctrl, ti_clkctrl_driver, ti_clkctrl_methods,
  345     sizeof(struct ti_clkctrl_softc));
  346 
  347 EARLY_DRIVER_MODULE(ti_clkctrl, simplebus, ti_clkctrl_driver, 0, 0,
  348     BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
  349 
  350 MODULE_VERSION(ti_clkctrl, 1);

Cache object: 9ef9c15e445ca195580606508a1727b7


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