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_mipi.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_mipi.h>
   39 
   40 #include "clkdev_if.h"
   41 
   42 /* #define      dprintf(format, arg...) printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg) */
   43 #define dprintf(format, arg...)
   44 
   45 /*
   46  * clknode for PLL_MIPI :
   47  *
   48  * clk = (pll_video0 * n * k) / m when vfb_sel=0
   49  * clk depend on sint_frac, sdiv2, s6p25_7p5, pll_feedback_div when vfb_sel=1
   50  *
   51  */
   52 
   53 struct aw_clk_mipi_sc {
   54         uint32_t        offset;
   55 
   56         struct aw_clk_factor    k;
   57         struct aw_clk_factor    m;
   58         struct aw_clk_factor    n;
   59 
   60         uint64_t                min_freq;
   61         uint64_t                max_freq;
   62 
   63         uint32_t        gate_shift;
   64         uint32_t        lock_shift;
   65         uint32_t        lock_retries;
   66 
   67         uint32_t        flags;
   68 };
   69 
   70 #define WRITE4(_clk, off, val)                                          \
   71         CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
   72 #define READ4(_clk, off, val)                                           \
   73         CLKDEV_READ_4(clknode_get_device(_clk), off, val)
   74 #define DEVICE_LOCK(_clk)                                                       \
   75         CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
   76 #define DEVICE_UNLOCK(_clk)                                             \
   77         CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
   78 
   79 #define LDO1_EN_SHIFT   23
   80 #define LDO2_EN_SHIFT   22
   81 #define VFB_SEL_SHIFT   16
   82 
   83 static int
   84 aw_clk_mipi_init(struct clknode *clk, device_t dev)
   85 {
   86 
   87         clknode_init_parent_idx(clk, 0);
   88         return (0);
   89 }
   90 
   91 static int
   92 aw_clk_mipi_set_gate(struct clknode *clk, bool enable)
   93 {
   94         struct aw_clk_mipi_sc *sc;
   95         uint32_t val;
   96 
   97         sc = clknode_get_softc(clk);
   98 
   99         dprintf("%sabling gate\n", enable ? "En" : "Dis");
  100         DEVICE_LOCK(clk);
  101         READ4(clk, sc->offset, &val);
  102         if (enable) {
  103                 val |= (1 << sc->gate_shift);
  104                 val |= (1 << LDO1_EN_SHIFT);
  105                 val |= (1 << LDO2_EN_SHIFT);
  106         } else {
  107                 val &= ~(1 << sc->gate_shift);
  108                 val &= ~(1 << LDO1_EN_SHIFT);
  109                 val &= ~(1 << LDO2_EN_SHIFT);
  110         }
  111         WRITE4(clk, sc->offset, val);
  112         DEVICE_UNLOCK(clk);
  113 
  114         return (0);
  115 }
  116 
  117 static uint64_t
  118 aw_clk_mipi_find_best(struct aw_clk_mipi_sc *sc, uint64_t fparent, uint64_t *fout,
  119     uint32_t *factor_k, uint32_t *factor_m, uint32_t *factor_n)
  120 {
  121         uint64_t cur, best;
  122         uint32_t n, k, m;
  123 
  124         best = 0;
  125         *factor_n = 0;
  126         *factor_k = 0;
  127         *factor_m = 0;
  128 
  129         for (n = aw_clk_factor_get_min(&sc->n); n <= aw_clk_factor_get_max(&sc->n); n++) {
  130                 for (k = aw_clk_factor_get_min(&sc->k); k <= aw_clk_factor_get_max(&sc->k); k++) {
  131                         for (m = aw_clk_factor_get_min(&sc->m); m <= aw_clk_factor_get_max(&sc->m); m++) {
  132                                 cur = (fparent * n * k) / m;
  133                                 if ((*fout - cur) < (*fout - best)) {
  134                                         best = cur;
  135                                         *factor_n = n;
  136                                         *factor_k = k;
  137                                         *factor_m = m;
  138                                 }
  139                                 if (best == *fout)
  140                                         return (best);
  141                         }
  142                 }
  143         }
  144 
  145         return best;
  146 }
  147 
  148 static int
  149 aw_clk_mipi_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
  150     int flags, int *stop)
  151 {
  152         struct aw_clk_mipi_sc *sc;
  153         uint64_t best = 0;
  154         uint32_t best_k, best_m, best_n;
  155         uint32_t k, m, n;
  156         uint32_t val;
  157         uint32_t retry;
  158 
  159         sc = clknode_get_softc(clk);
  160 
  161         best = aw_clk_mipi_find_best(sc, fparent, fout, &best_k, &best_m, &best_n);
  162 
  163         if (best < sc->min_freq ||
  164             best > sc->max_freq) {
  165                 printf("%s: Cannot set %ju for %s (min=%ju max=%ju)\n",
  166                     __func__, best, clknode_get_name(clk),
  167                     sc->min_freq, sc->max_freq);
  168                 return (ERANGE);
  169         }
  170         if ((flags & CLK_SET_DRYRUN) != 0) {
  171                 *fout = best;
  172                 *stop = 1;
  173                 return (0);
  174         }
  175 
  176         DEVICE_LOCK(clk);
  177         READ4(clk, sc->offset, &val);
  178         /* Disable clock during freq changes */
  179         val &= ~(1 << sc->gate_shift);
  180         WRITE4(clk, sc->offset, val);
  181 
  182         k = aw_clk_factor_get_value(&sc->k, best_k);
  183         n = aw_clk_factor_get_value(&sc->n, best_n);
  184         m = aw_clk_factor_get_value(&sc->m, best_m);
  185         val &= ~sc->k.mask;
  186         val &= ~sc->m.mask;
  187         val &= ~sc->n.mask;
  188         val |= k << sc->k.shift;
  189         val |= m << sc->m.shift;
  190         val |= n << sc->n.shift;
  191 
  192         /* Write the clock changes */
  193         WRITE4(clk, sc->offset, val);
  194 
  195         /* Enable clock now that we've change it */
  196         val |= 1 << sc->gate_shift;
  197         WRITE4(clk, sc->offset, val);
  198         DEVICE_UNLOCK(clk);
  199 
  200         for (retry = 0; retry < sc->lock_retries; retry++) {
  201                 READ4(clk, sc->offset, &val);
  202                 if ((val & (1 << sc->lock_shift)) != 0)
  203                         break;
  204                 DELAY(1000);
  205         }
  206 
  207         *fout = best;
  208         *stop = 1;
  209 
  210         return (0);
  211 }
  212 
  213 static int
  214 aw_clk_mipi_recalc(struct clknode *clk, uint64_t *freq)
  215 {
  216         struct aw_clk_mipi_sc *sc;
  217         uint32_t val, m, n, k;
  218 
  219         sc = clknode_get_softc(clk);
  220 
  221         DEVICE_LOCK(clk);
  222         READ4(clk, sc->offset, &val);
  223         DEVICE_UNLOCK(clk);
  224 
  225         k = aw_clk_get_factor(val, &sc->k);
  226         m = aw_clk_get_factor(val, &sc->m);
  227         n = aw_clk_get_factor(val, &sc->n);
  228 
  229         *freq = (*freq * n * k) / m;
  230 
  231         return (0);
  232 }
  233 
  234 static clknode_method_t aw_mipi_clknode_methods[] = {
  235         /* Device interface */
  236         CLKNODEMETHOD(clknode_init,             aw_clk_mipi_init),
  237         CLKNODEMETHOD(clknode_set_gate,         aw_clk_mipi_set_gate),
  238         CLKNODEMETHOD(clknode_recalc_freq,      aw_clk_mipi_recalc),
  239         CLKNODEMETHOD(clknode_set_freq,         aw_clk_mipi_set_freq),
  240         CLKNODEMETHOD_END
  241 };
  242 
  243 DEFINE_CLASS_1(aw_mipi_clknode, aw_mipi_clknode_class, aw_mipi_clknode_methods,
  244     sizeof(struct aw_clk_mipi_sc), clknode_class);
  245 
  246 int
  247 aw_clk_mipi_register(struct clkdom *clkdom, struct aw_clk_mipi_def *clkdef)
  248 {
  249         struct clknode *clk;
  250         struct aw_clk_mipi_sc *sc;
  251 
  252         clk = clknode_create(clkdom, &aw_mipi_clknode_class, &clkdef->clkdef);
  253         if (clk == NULL)
  254                 return (1);
  255 
  256         sc = clknode_get_softc(clk);
  257 
  258         sc->offset = clkdef->offset;
  259 
  260         sc->k.shift = clkdef->k.shift;
  261         sc->k.width = clkdef->k.width;
  262         sc->k.mask = ((1 << sc->k.width) - 1) << sc->k.shift;
  263         sc->k.value = clkdef->k.value;
  264         sc->k.flags = clkdef->k.flags;
  265         sc->k.min_value = clkdef->k.min_value;
  266 
  267         sc->m.shift = clkdef->m.shift;
  268         sc->m.width = clkdef->m.width;
  269         sc->m.mask = ((1 << sc->m.width) - 1) << sc->m.shift;
  270         sc->m.value = clkdef->m.value;
  271         sc->m.flags = clkdef->m.flags;
  272         sc->m.min_value = clkdef->m.min_value;
  273 
  274         sc->n.shift = clkdef->n.shift;
  275         sc->n.width = clkdef->n.width;
  276         sc->n.mask = ((1 << sc->n.width) - 1) << sc->n.shift;
  277         sc->n.value = clkdef->n.value;
  278         sc->n.flags = clkdef->n.flags;
  279         sc->n.min_value = clkdef->n.min_value;
  280 
  281         sc->min_freq = clkdef->min_freq;
  282         sc->max_freq = clkdef->max_freq;
  283 
  284         sc->gate_shift = clkdef->gate_shift;
  285 
  286         sc->lock_shift = clkdef->lock_shift;
  287         sc->lock_retries = clkdef->lock_retries;
  288 
  289         sc->flags = clkdef->flags;
  290 
  291         clknode_register(clkdom, clk);
  292 
  293         return (0);
  294 }

Cache object: af32d4e165692c4e5e4f2499980babfb


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