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/allwinner/clkng/aw_clk_nm.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) 2017 Emmanuel Vadot <manu@freebsd.org>
    3  *
    4  * Redistribution and use in source and binary forms, with or without
    5  * modification, are permitted provided that the following conditions
    6  * are met:
    7  * 1. Redistributions of source code must retain the above copyright
    8  *    notice, this list of conditions and the following disclaimer.
    9  * 2. Redistributions in binary form must reproduce the above copyright
   10  *    notice, this list of conditions and the following disclaimer in the
   11  *    documentation and/or other materials provided with the distribution.
   12  *
   13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   18  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
   20  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   21  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   23  * SUCH DAMAGE.
   24  *
   25  * $FreeBSD$
   26  */
   27 
   28 #include <sys/cdefs.h>
   29 __FBSDID("$FreeBSD$");
   30 
   31 #include <sys/param.h>
   32 #include <sys/systm.h>
   33 #include <sys/bus.h>
   34 
   35 #include <dev/extres/clk/clk.h>
   36 
   37 #include <arm/allwinner/clkng/aw_clk.h>
   38 #include <arm/allwinner/clkng/aw_clk_nm.h>
   39 
   40 #include "clkdev_if.h"
   41 
   42 /*
   43  * clknode for clocks matching the formula :
   44  *
   45  * clk = clkin / n / m
   46  *
   47  */
   48 
   49 struct aw_clk_nm_sc {
   50         uint32_t        offset;
   51 
   52         struct aw_clk_factor    m;
   53         struct aw_clk_factor    n;
   54         struct aw_clk_factor    prediv;
   55 
   56         uint32_t        mux_shift;
   57         uint32_t        mux_mask;
   58         uint32_t        gate_shift;
   59         uint32_t        lock_shift;
   60         uint32_t        lock_retries;
   61 
   62         uint32_t        flags;
   63 };
   64 
   65 #define WRITE4(_clk, off, val)                                          \
   66         CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
   67 #define READ4(_clk, off, val)                                           \
   68         CLKDEV_READ_4(clknode_get_device(_clk), off, val)
   69 #define DEVICE_LOCK(_clk)                                                       \
   70         CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
   71 #define DEVICE_UNLOCK(_clk)                                             \
   72         CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
   73 
   74 static int
   75 aw_clk_nm_init(struct clknode *clk, device_t dev)
   76 {
   77         struct aw_clk_nm_sc *sc;
   78         uint32_t val, idx;
   79 
   80         sc = clknode_get_softc(clk);
   81 
   82         idx = 0;
   83         if ((sc->flags & AW_CLK_HAS_MUX) != 0) {
   84                 DEVICE_LOCK(clk);
   85                 READ4(clk, sc->offset, &val);
   86                 DEVICE_UNLOCK(clk);
   87 
   88                 idx = (val & sc->mux_mask) >> sc->mux_shift;
   89         }
   90 
   91         clknode_init_parent_idx(clk, idx);
   92         return (0);
   93 }
   94 
   95 static int
   96 aw_clk_nm_set_gate(struct clknode *clk, bool enable)
   97 {
   98         struct aw_clk_nm_sc *sc;
   99         uint32_t val;
  100 
  101         sc = clknode_get_softc(clk);
  102 
  103         if ((sc->flags & AW_CLK_HAS_GATE) == 0)
  104                 return (0);
  105 
  106         DEVICE_LOCK(clk);
  107         READ4(clk, sc->offset, &val);
  108         if (enable)
  109                 val |= (1 << sc->gate_shift);
  110         else
  111                 val &= ~(1 << sc->gate_shift);
  112         WRITE4(clk, sc->offset, val);
  113         DEVICE_UNLOCK(clk);
  114 
  115         return (0);
  116 }
  117 
  118 static int
  119 aw_clk_nm_set_mux(struct clknode *clk, int index)
  120 {
  121         struct aw_clk_nm_sc *sc;
  122         uint32_t val;
  123 
  124         sc = clknode_get_softc(clk);
  125 
  126         if ((sc->flags & AW_CLK_HAS_MUX) == 0)
  127                 return (0);
  128 
  129         DEVICE_LOCK(clk);
  130         READ4(clk, sc->offset, &val);
  131         val &= ~sc->mux_mask;
  132         val |= index << sc->mux_shift;
  133         WRITE4(clk, sc->offset, val);
  134         DEVICE_UNLOCK(clk);
  135 
  136         return (0);
  137 }
  138 
  139 static uint64_t
  140 aw_clk_nm_find_best(struct aw_clk_nm_sc *sc, uint64_t fparent, uint64_t *fout,
  141     uint32_t *factor_n, uint32_t *factor_m)
  142 {
  143         uint64_t cur, best;
  144         uint32_t m, n, max_m, max_n, min_m, min_n;
  145 
  146         *factor_n = *factor_m = 0;
  147 
  148         max_m = aw_clk_factor_get_max(&sc->m);
  149         max_n = aw_clk_factor_get_max(&sc->n);
  150         min_m = aw_clk_factor_get_min(&sc->m);
  151         min_n = aw_clk_factor_get_min(&sc->n);
  152 
  153         for (m = min_m; m <= max_m; ) {
  154                 for (n = min_n; n <= max_n; ) {
  155                         cur = fparent / n / m;
  156                         if (clk_freq_diff(*fout, cur) <
  157                             clk_freq_diff(*fout, best)) {
  158                                 best = cur;
  159                                 *factor_n = n;
  160                                 *factor_m = m;
  161                         }
  162 
  163                         if ((sc->n.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)
  164                                 n <<= 1;
  165                         else
  166                                 n++;
  167                 }
  168                 if ((sc->m.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)
  169                         m <<= 1;
  170                 else
  171                         m++;
  172         }
  173 
  174         return (best);
  175 }
  176 
  177 static int
  178 aw_clk_nm_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
  179     int flags, int *stop)
  180 {
  181         struct aw_clk_nm_sc *sc;
  182         struct clknode *p_clk;
  183         const char **p_names;
  184         uint64_t cur, best;
  185         uint32_t val, m, n, best_m, best_n;
  186         int p_idx, best_parent, retry;
  187 
  188         sc = clknode_get_softc(clk);
  189 
  190         best = cur = 0;
  191         best_parent = 0;
  192 
  193         if ((sc->flags & AW_CLK_REPARENT) != 0) {
  194                 p_names = clknode_get_parent_names(clk);
  195                 for (p_idx = 0; p_idx != clknode_get_parents_num(clk); p_idx++) {
  196                         p_clk = clknode_find_by_name(p_names[p_idx]);
  197                         clknode_get_freq(p_clk, &fparent);
  198 
  199                         cur = aw_clk_nm_find_best(sc, fparent, fout, &n, &m);
  200                         if (clk_freq_diff(*fout, cur) <
  201                             clk_freq_diff(*fout, best)) {
  202                                 best = cur;
  203                                 best_parent = p_idx;
  204                                 best_n = n;
  205                                 best_m = m;
  206                         }
  207                 }
  208 
  209                 p_idx = clknode_get_parent_idx(clk);
  210                 p_clk = clknode_get_parent(clk);
  211                 clknode_get_freq(p_clk, &fparent);
  212         } else {
  213                 best = aw_clk_nm_find_best(sc, fparent, fout,
  214                     &best_n, &best_m);
  215         }
  216 
  217         if ((flags & CLK_SET_DRYRUN) != 0) {
  218                 *fout = best;
  219                 *stop = 1;
  220                 return (0);
  221         }
  222 
  223         if ((best < *fout) &&
  224           ((flags & CLK_SET_ROUND_DOWN) == 0)) {
  225                 *stop = 1;
  226                 printf("best freq (%ju) < requested freq(%ju)\n",
  227                     best, *fout);
  228                 return (ERANGE);
  229         }
  230         if ((best > *fout) &&
  231           ((flags & CLK_SET_ROUND_UP) == 0)) {
  232                 *stop = 1;
  233                 printf("best freq (%ju) > requested freq(%ju)\n",
  234                     best, *fout);
  235                 return (ERANGE);
  236         }
  237 
  238         if ((sc->flags & AW_CLK_REPARENT) != 0 && p_idx != best_parent)
  239                 clknode_set_parent_by_idx(clk, best_parent);
  240 
  241         DEVICE_LOCK(clk);
  242         READ4(clk, sc->offset, &val);
  243 
  244         n = aw_clk_factor_get_value(&sc->n, best_n);
  245         m = aw_clk_factor_get_value(&sc->m, best_m);
  246         val &= ~sc->n.mask;
  247         val &= ~sc->m.mask;
  248         val |= n << sc->n.shift;
  249         val |= m << sc->m.shift;
  250 
  251         WRITE4(clk, sc->offset, val);
  252         DEVICE_UNLOCK(clk);
  253 
  254         if ((sc->flags & AW_CLK_HAS_LOCK) != 0) {
  255                 for (retry = 0; retry < sc->lock_retries; retry++) {
  256                         READ4(clk, sc->offset, &val);
  257                         if ((val & (1 << sc->lock_shift)) != 0)
  258                                 break;
  259                         DELAY(1000);
  260                 }
  261         }
  262 
  263         *fout = best;
  264         *stop = 1;
  265 
  266         return (0);
  267 }
  268 
  269 static int
  270 aw_clk_nm_recalc(struct clknode *clk, uint64_t *freq)
  271 {
  272         struct aw_clk_nm_sc *sc;
  273         uint32_t val, m, n, prediv;
  274 
  275         sc = clknode_get_softc(clk);
  276 
  277         DEVICE_LOCK(clk);
  278         READ4(clk, sc->offset, &val);
  279         DEVICE_UNLOCK(clk);
  280 
  281         m = aw_clk_get_factor(val, &sc->m);
  282         n = aw_clk_get_factor(val, &sc->n);
  283         if (sc->flags & AW_CLK_HAS_PREDIV)
  284                 prediv = aw_clk_get_factor(val, &sc->prediv);
  285         else
  286                 prediv = 1;
  287 
  288         *freq = *freq / prediv / n / m;
  289 
  290         return (0);
  291 }
  292 
  293 static clknode_method_t aw_nm_clknode_methods[] = {
  294         /* Device interface */
  295         CLKNODEMETHOD(clknode_init,             aw_clk_nm_init),
  296         CLKNODEMETHOD(clknode_set_gate,         aw_clk_nm_set_gate),
  297         CLKNODEMETHOD(clknode_set_mux,          aw_clk_nm_set_mux),
  298         CLKNODEMETHOD(clknode_recalc_freq,      aw_clk_nm_recalc),
  299         CLKNODEMETHOD(clknode_set_freq,         aw_clk_nm_set_freq),
  300         CLKNODEMETHOD_END
  301 };
  302 
  303 DEFINE_CLASS_1(aw_nm_clknode, aw_nm_clknode_class, aw_nm_clknode_methods,
  304     sizeof(struct aw_clk_nm_sc), clknode_class);
  305 
  306 int
  307 aw_clk_nm_register(struct clkdom *clkdom, struct aw_clk_nm_def *clkdef)
  308 {
  309         struct clknode *clk;
  310         struct aw_clk_nm_sc *sc;
  311 
  312         clk = clknode_create(clkdom, &aw_nm_clknode_class, &clkdef->clkdef);
  313         if (clk == NULL)
  314                 return (1);
  315 
  316         sc = clknode_get_softc(clk);
  317 
  318         sc->offset = clkdef->offset;
  319 
  320         sc->m.shift = clkdef->m.shift;
  321         sc->m.width = clkdef->m.width;
  322         sc->m.mask = ((1 << sc->m.width) - 1) << sc->m.shift;
  323         sc->m.value = clkdef->m.value;
  324         sc->m.flags = clkdef->m.flags;
  325 
  326         sc->n.shift = clkdef->n.shift;
  327         sc->n.width = clkdef->n.width;
  328         sc->n.mask = ((1 << sc->n.width) - 1) << sc->n.shift;
  329         sc->n.value = clkdef->n.value;
  330         sc->n.flags = clkdef->n.flags;
  331 
  332         sc->prediv.shift = clkdef->prediv.shift;
  333         sc->prediv.width = clkdef->prediv.width;
  334         sc->prediv.mask = ((1 << sc->prediv.width) - 1) << sc->prediv.shift;
  335         sc->prediv.value = clkdef->prediv.value;
  336         sc->prediv.flags = clkdef->prediv.flags;
  337         sc->prediv.cond_shift = clkdef->prediv.cond_shift;
  338         if (clkdef->prediv.cond_width != 0)
  339                 sc->prediv.cond_mask = ((1 << clkdef->prediv.cond_width) - 1) << sc->prediv.shift;
  340         else
  341                 sc->prediv.cond_mask = clkdef->prediv.cond_mask;
  342         sc->prediv.cond_value = clkdef->prediv.cond_value;
  343 
  344         sc->mux_shift = clkdef->mux_shift;
  345         sc->mux_mask = ((1 << clkdef->mux_width) - 1) << sc->mux_shift;
  346 
  347         sc->gate_shift = clkdef->gate_shift;
  348 
  349         sc->lock_shift = clkdef->lock_shift;
  350         sc->lock_retries = clkdef->lock_retries;
  351 
  352         sc->flags = clkdef->flags;
  353 
  354         clknode_register(clkdom, clk);
  355 
  356         return (0);
  357 }

Cache object: 1f35ab5cf694891d967edc107367653f


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