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_m.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) 2019 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_m.h>
   39 
   40 #include "clkdev_if.h"
   41 
   42 /*
   43  * clknode for clocks matching the formula :
   44  *
   45  * clk = clkin / m
   46  * And that needs to potentially :
   47  * 1) Set the parent freq
   48  * 2) Support Setting the parent to a multiple
   49  *
   50  */
   51 
   52 struct aw_clk_m_sc {
   53         uint32_t        offset;
   54 
   55         struct aw_clk_factor    m;
   56 
   57         uint32_t        mux_shift;
   58         uint32_t        mux_mask;
   59         uint32_t        gate_shift;
   60 
   61         uint32_t        flags;
   62 };
   63 
   64 #define WRITE4(_clk, off, val)                                          \
   65         CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
   66 #define READ4(_clk, off, val)                                           \
   67         CLKDEV_READ_4(clknode_get_device(_clk), off, val)
   68 #define DEVICE_LOCK(_clk)                                                       \
   69         CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
   70 #define DEVICE_UNLOCK(_clk)                                             \
   71         CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
   72 
   73 static int
   74 aw_clk_m_init(struct clknode *clk, device_t dev)
   75 {
   76         struct aw_clk_m_sc *sc;
   77         uint32_t val, idx;
   78 
   79         sc = clknode_get_softc(clk);
   80 
   81         idx = 0;
   82         if ((sc->flags & AW_CLK_HAS_MUX) != 0) {
   83                 DEVICE_LOCK(clk);
   84                 READ4(clk, sc->offset, &val);
   85                 DEVICE_UNLOCK(clk);
   86 
   87                 idx = (val & sc->mux_mask) >> sc->mux_shift;
   88         }
   89 
   90         clknode_init_parent_idx(clk, idx);
   91         return (0);
   92 }
   93 
   94 static int
   95 aw_clk_m_set_gate(struct clknode *clk, bool enable)
   96 {
   97         struct aw_clk_m_sc *sc;
   98         uint32_t val;
   99 
  100         sc = clknode_get_softc(clk);
  101 
  102         if ((sc->flags & AW_CLK_HAS_GATE) == 0)
  103                 return (0);
  104 
  105         DEVICE_LOCK(clk);
  106         READ4(clk, sc->offset, &val);
  107         if (enable)
  108                 val |= (1 << sc->gate_shift);
  109         else
  110                 val &= ~(1 << sc->gate_shift);
  111         WRITE4(clk, sc->offset, val);
  112         DEVICE_UNLOCK(clk);
  113 
  114         return (0);
  115 }
  116 
  117 static int
  118 aw_clk_m_set_mux(struct clknode *clk, int index)
  119 {
  120         struct aw_clk_m_sc *sc;
  121         uint32_t val;
  122 
  123         sc = clknode_get_softc(clk);
  124 
  125         if ((sc->flags & AW_CLK_HAS_MUX) == 0)
  126                 return (0);
  127 
  128         DEVICE_LOCK(clk);
  129         READ4(clk, sc->offset, &val);
  130         val &= ~sc->mux_mask;
  131         val |= index << sc->mux_shift;
  132         WRITE4(clk, sc->offset, val);
  133         DEVICE_UNLOCK(clk);
  134 
  135         return (0);
  136 }
  137 
  138 static uint64_t
  139 aw_clk_m_find_best(struct aw_clk_m_sc *sc, uint64_t fparent, uint64_t *fout,
  140     uint32_t *factor_m)
  141 {
  142         uint64_t cur, best;
  143         uint32_t m, max_m, min_m;
  144 
  145         *factor_m = 0;
  146 
  147         max_m = aw_clk_factor_get_max(&sc->m);
  148         min_m = aw_clk_factor_get_min(&sc->m);
  149 
  150         for (m = min_m; m <= max_m; ) {
  151                 cur = fparent / m;
  152                 if (abs(*fout - cur) < abs(*fout - best)) {
  153                         best = cur;
  154                         *factor_m = m;
  155                 }
  156                 if ((sc->m.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)
  157                         m <<= 1;
  158                 else
  159                         m++;
  160         }
  161 
  162         return (best);
  163 }
  164 
  165 static int
  166 aw_clk_m_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
  167     int flags, int *stop)
  168 {
  169         struct aw_clk_m_sc *sc;
  170         struct clknode *p_clk;
  171         uint64_t cur, best;
  172         uint32_t val, m, best_m;
  173 
  174         sc = clknode_get_softc(clk);
  175 
  176         best = cur = 0;
  177 
  178         best = aw_clk_m_find_best(sc, fparent, fout,
  179             &best_m);
  180         if ((best != *fout) && ((sc->flags & AW_CLK_SET_PARENT) != 0)) {
  181                 p_clk = clknode_get_parent(clk);
  182                 if (p_clk == NULL) {
  183                         printf("%s: Cannot get parent for clock %s\n",
  184                             __func__,
  185                             clknode_get_name(clk));
  186                         return (ENXIO);
  187                 }
  188                 clknode_set_freq(p_clk, *fout, CLK_SET_ROUND_MULTIPLE, 0);
  189                 clknode_get_freq(p_clk, &fparent);
  190                 best = aw_clk_m_find_best(sc, fparent, fout,
  191                     &best_m);
  192         }
  193 
  194         if ((flags & CLK_SET_DRYRUN) != 0) {
  195                 *fout = best;
  196                 *stop = 1;
  197                 return (0);
  198         }
  199 
  200         if ((best < *fout) &&
  201           ((flags & CLK_SET_ROUND_DOWN) == 0)) {
  202                 *stop = 1;
  203                 return (ERANGE);
  204         }
  205         if ((best > *fout) &&
  206           ((flags & CLK_SET_ROUND_UP) == 0)) {
  207                 *stop = 1;
  208                 return (ERANGE);
  209         }
  210 
  211         DEVICE_LOCK(clk);
  212         READ4(clk, sc->offset, &val);
  213 
  214         m = aw_clk_factor_get_value(&sc->m, best_m);
  215         val &= ~sc->m.mask;
  216         val |= m << sc->m.shift;
  217 
  218         WRITE4(clk, sc->offset, val);
  219         DEVICE_UNLOCK(clk);
  220 
  221         *fout = best;
  222         *stop = 1;
  223 
  224         return (0);
  225 }
  226 
  227 static int
  228 aw_clk_m_recalc(struct clknode *clk, uint64_t *freq)
  229 {
  230         struct aw_clk_m_sc *sc;
  231         uint32_t val, m;
  232 
  233         sc = clknode_get_softc(clk);
  234 
  235         DEVICE_LOCK(clk);
  236         READ4(clk, sc->offset, &val);
  237         DEVICE_UNLOCK(clk);
  238 
  239         m = aw_clk_get_factor(val, &sc->m);
  240 
  241         *freq = *freq / m;
  242 
  243         return (0);
  244 }
  245 
  246 static clknode_method_t aw_m_clknode_methods[] = {
  247         /* Device interface */
  248         CLKNODEMETHOD(clknode_init,             aw_clk_m_init),
  249         CLKNODEMETHOD(clknode_set_gate,         aw_clk_m_set_gate),
  250         CLKNODEMETHOD(clknode_set_mux,          aw_clk_m_set_mux),
  251         CLKNODEMETHOD(clknode_recalc_freq,      aw_clk_m_recalc),
  252         CLKNODEMETHOD(clknode_set_freq,         aw_clk_m_set_freq),
  253         CLKNODEMETHOD_END
  254 };
  255 
  256 DEFINE_CLASS_1(aw_m_clknode, aw_m_clknode_class, aw_m_clknode_methods,
  257     sizeof(struct aw_clk_m_sc), clknode_class);
  258 
  259 int
  260 aw_clk_m_register(struct clkdom *clkdom, struct aw_clk_m_def *clkdef)
  261 {
  262         struct clknode *clk;
  263         struct aw_clk_m_sc *sc;
  264 
  265         clk = clknode_create(clkdom, &aw_m_clknode_class, &clkdef->clkdef);
  266         if (clk == NULL)
  267                 return (1);
  268 
  269         sc = clknode_get_softc(clk);
  270 
  271         sc->offset = clkdef->offset;
  272 
  273         sc->m.shift = clkdef->m.shift;
  274         sc->m.width = clkdef->m.width;
  275         sc->m.mask = ((1 << sc->m.width) - 1) << sc->m.shift;
  276         sc->m.value = clkdef->m.value;
  277         sc->m.flags = clkdef->m.flags;
  278 
  279         sc->mux_shift = clkdef->mux_shift;
  280         sc->mux_mask = ((1 << clkdef->mux_width) - 1) << sc->mux_shift;
  281 
  282         sc->gate_shift = clkdef->gate_shift;
  283 
  284         sc->flags = clkdef->flags;
  285 
  286         clknode_register(clkdom, clk);
  287 
  288         return (0);
  289 }

Cache object: 95f6e92f182748d0a7a3853256aff5cd


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