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/dev/extres/clk/clk_div.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 2016 Michal Meloun <mmel@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 #include <sys/cdefs.h>
   28 __FBSDID("$FreeBSD$");
   29 
   30 
   31 #include <sys/param.h>
   32 #include <sys/conf.h>
   33 #include <sys/bus.h>
   34 #include <sys/kernel.h>
   35 #include <sys/systm.h>
   36 
   37 #include <machine/bus.h>
   38 
   39 #include <dev/extres/clk/clk_div.h>
   40 
   41 #include "clkdev_if.h"
   42 
   43 #define WR4(_clk, off, val)                                             \
   44         CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
   45 #define RD4(_clk, off, val)                                             \
   46         CLKDEV_READ_4(clknode_get_device(_clk), off, val)
   47 #define MD4(_clk, off, clr, set )                                       \
   48         CLKDEV_MODIFY_4(clknode_get_device(_clk), off, clr, set)
   49 #define DEVICE_LOCK(_clk)                                                       \
   50         CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
   51 #define DEVICE_UNLOCK(_clk)                                             \
   52         CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
   53 
   54 static int clknode_div_init(struct clknode *clk, device_t dev);
   55 static int clknode_div_recalc(struct clknode *clk, uint64_t *req);
   56 static int clknode_div_set_freq(struct clknode *clknode, uint64_t fin,
   57     uint64_t *fout, int flag, int *stop);
   58 
   59 struct clknode_div_sc {
   60         struct mtx      *mtx;
   61         struct resource *mem_res;
   62         uint32_t        offset;
   63         uint32_t        i_shift;
   64         uint32_t        i_mask;
   65         uint32_t        i_width;
   66         uint32_t        f_shift;
   67         uint32_t        f_mask;
   68         uint32_t        f_width;
   69         int             div_flags;
   70         uint32_t        divider;        /* in natural form */
   71 
   72         struct clk_div_table    *div_table;
   73 };
   74 
   75 static clknode_method_t clknode_div_methods[] = {
   76         /* Device interface */
   77         CLKNODEMETHOD(clknode_init,             clknode_div_init),
   78         CLKNODEMETHOD(clknode_recalc_freq,      clknode_div_recalc),
   79         CLKNODEMETHOD(clknode_set_freq,         clknode_div_set_freq),
   80         CLKNODEMETHOD_END
   81 };
   82 DEFINE_CLASS_1(clknode_div, clknode_div_class, clknode_div_methods,
   83    sizeof(struct clknode_div_sc), clknode_class);
   84 
   85 static uint32_t
   86 clknode_div_table_get_divider(struct clknode_div_sc *sc, uint32_t divider)
   87 {
   88         struct clk_div_table *table;
   89 
   90         if (!(sc->div_flags & CLK_DIV_WITH_TABLE))
   91                 return (divider);
   92 
   93         for (table = sc->div_table; table->divider != 0; table++)
   94                 if (table->value == sc->divider)
   95                         return (table->divider);
   96 
   97         return (0);
   98 }
   99 
  100 static int
  101 clknode_div_table_get_value(struct clknode_div_sc *sc, uint32_t *divider)
  102 {
  103         struct clk_div_table *table;
  104 
  105         if (!(sc->div_flags & CLK_DIV_WITH_TABLE))
  106                 return (0);
  107 
  108         for (table = sc->div_table; table->divider != 0; table++)
  109                 if (table->divider == *divider) {
  110                         *divider = table->value;
  111                         return (0);
  112                 }
  113 
  114         return (ENOENT);
  115 }
  116 
  117 static int
  118 clknode_div_init(struct clknode *clk, device_t dev)
  119 {
  120         uint32_t reg;
  121         struct clknode_div_sc *sc;
  122         uint32_t i_div, f_div;
  123         int rv;
  124 
  125         sc = clknode_get_softc(clk);
  126 
  127         DEVICE_LOCK(clk);
  128         rv = RD4(clk, sc->offset, &reg);
  129         DEVICE_UNLOCK(clk);
  130         if (rv != 0)
  131                 return (rv);
  132 
  133         i_div = (reg >> sc->i_shift) & sc->i_mask;
  134         if (!(sc->div_flags & CLK_DIV_WITH_TABLE) &&
  135             !(sc->div_flags & CLK_DIV_ZERO_BASED))
  136                 i_div++;
  137         f_div = (reg >> sc->f_shift) & sc->f_mask;
  138         sc->divider = i_div << sc->f_width | f_div;
  139 
  140         sc->divider = clknode_div_table_get_divider(sc, sc->divider);
  141         if (sc->divider == 0)
  142                 panic("%s: divider is zero!\n", clknode_get_name(clk));
  143 
  144         clknode_init_parent_idx(clk, 0);
  145         return(0);
  146 }
  147 
  148 static int
  149 clknode_div_recalc(struct clknode *clk, uint64_t *freq)
  150 {
  151         struct clknode_div_sc *sc;
  152 
  153         sc = clknode_get_softc(clk);
  154         if (sc->divider == 0) {
  155                 printf("%s: %s divider is zero!\n", clknode_get_name(clk),
  156                 __func__);
  157                 *freq = 0;
  158                 return(EINVAL);
  159         }
  160         *freq = (*freq << sc->f_width) / sc->divider;
  161         return (0);
  162 }
  163 
  164 static int
  165 clknode_div_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
  166   int flags, int *stop)
  167 {
  168         struct clknode_div_sc *sc;
  169         uint64_t divider, _fin, _fout;
  170         uint32_t reg, i_div, f_div, hw_i_div;
  171         int rv;
  172 
  173         sc = clknode_get_softc(clk);
  174 
  175         /* For fractional divider. */
  176         _fin = fin << sc->f_width;
  177         divider = (_fin + *fout / 2) / *fout;
  178         _fout = _fin / divider;
  179 
  180         /* Rounding. */
  181         if ((flags & CLK_SET_ROUND_UP) && (*fout < _fout))
  182                 divider--;
  183         else if ((flags & CLK_SET_ROUND_DOWN) && (*fout > _fout))
  184                 divider++;
  185 
  186         /* Break divider into integer and fractional parts. */
  187         i_div = divider >> sc->f_width;
  188         f_div = divider  & sc->f_mask;
  189 
  190         if (i_div == 0) {
  191                 printf("%s: %s integer divider is zero!\n",
  192                      clknode_get_name(clk), __func__);
  193                 return(EINVAL);
  194         }
  195 
  196         *stop = 1;
  197         hw_i_div = i_div;
  198         if (sc->div_flags & CLK_DIV_WITH_TABLE) {
  199                 if (clknode_div_table_get_value(sc, &hw_i_div) != 0)
  200                                 return (ERANGE);
  201         } else {
  202                 if (!(sc->div_flags & CLK_DIV_ZERO_BASED))
  203                         hw_i_div--;
  204 
  205                 if (i_div > sc->i_mask) {
  206                         /* XXX Pass to parent or return error? */
  207                         printf("%s: %s integer divider is too big: %u\n",
  208                             clknode_get_name(clk), __func__, i_div);
  209                         hw_i_div = sc->i_mask;
  210                         *stop = 0;
  211                 }
  212                 i_div = hw_i_div;
  213                 if (!(sc->div_flags & CLK_DIV_ZERO_BASED))
  214                         i_div++;
  215         }
  216 
  217         divider = i_div << sc->f_width | f_div;
  218 
  219         if ((flags & CLK_SET_DRYRUN) == 0) {
  220                 if ((*stop != 0) &&
  221                     ((flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0) &&
  222                     (*fout != (_fin / divider)))
  223                         return (ERANGE);
  224 
  225                 DEVICE_LOCK(clk);
  226                 rv = MD4(clk, sc->offset,
  227                     (sc->i_mask << sc->i_shift) | (sc->f_mask << sc->f_shift),
  228                     (hw_i_div << sc->i_shift) | (f_div << sc->f_shift));
  229                 if (rv != 0) {
  230                         DEVICE_UNLOCK(clk);
  231                         return (rv);
  232                 }
  233                 RD4(clk, sc->offset, &reg);
  234                 DEVICE_UNLOCK(clk);
  235 
  236                 sc->divider = divider;
  237         }
  238 
  239         *fout = _fin / divider;
  240         return (0);
  241 }
  242 
  243 int
  244 clknode_div_register(struct clkdom *clkdom, struct clk_div_def *clkdef)
  245 {
  246         struct clknode *clk;
  247         struct clknode_div_sc *sc;
  248 
  249         clk = clknode_create(clkdom, &clknode_div_class, &clkdef->clkdef);
  250         if (clk == NULL)
  251                 return (1);
  252 
  253         sc = clknode_get_softc(clk);
  254         sc->offset = clkdef->offset;
  255         sc->i_shift = clkdef->i_shift;
  256         sc->i_width = clkdef->i_width;
  257         sc->i_mask = (1 << clkdef->i_width) - 1;
  258         sc->f_shift = clkdef->f_shift;
  259         sc->f_width = clkdef->f_width;
  260         sc->f_mask = (1 << clkdef->f_width) - 1;
  261         sc->div_flags = clkdef->div_flags;
  262         sc->div_table = clkdef->div_table;
  263 
  264         clknode_register(clkdom, clk);
  265         return (0);
  266 }

Cache object: 1fa5995d2772026641d39c7800294995


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