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/mips/ingenic/jz4780_clk_gen.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 2015 Alexander Kabaev <kan@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 /*
   28  * Ingenic JZ4780 generic CGU clock driver.
   29  *
   30  */
   31 
   32 #include <sys/cdefs.h>
   33 __FBSDID("$FreeBSD: releng/12.0/sys/mips/ingenic/jz4780_clk_gen.c 310307 2016-12-20 01:37:00Z jmcneill $");
   34 
   35 #include <sys/param.h>
   36 #include <sys/systm.h>
   37 #include <sys/conf.h>
   38 #include <sys/bus.h>
   39 #include <sys/lock.h>
   40 #include <sys/mutex.h>
   41 #include <sys/resource.h>
   42 
   43 #include <machine/bus.h>
   44 
   45 #include <mips/ingenic/jz4780_clk.h>
   46 #include <mips/ingenic/jz4780_regs.h>
   47 
   48 /* JZ4780 generic mux and div clocks implementation */
   49 static int jz4780_clk_gen_init(struct clknode *clk, device_t dev);
   50 static int jz4780_clk_gen_recalc_freq(struct clknode *clk, uint64_t *freq);
   51 static int jz4780_clk_gen_set_freq(struct clknode *clk, uint64_t fin,
   52     uint64_t *fout, int flags, int *stop);
   53 static int jz4780_clk_gen_set_gate(struct clknode *clk, bool enable);
   54 static int jz4780_clk_gen_set_mux(struct clknode *clk, int src);
   55 
   56 struct jz4780_clk_gen_sc {
   57         struct mtx      *clk_mtx;
   58         struct resource *clk_res;
   59         int clk_reg;
   60         const struct jz4780_clk_descr *clk_descr;
   61 };
   62 
   63 /*
   64  * JZ4780 clock PLL clock methods
   65  */
   66 static clknode_method_t jz4780_clk_gen_methods[] = {
   67         CLKNODEMETHOD(clknode_init,             jz4780_clk_gen_init),
   68         CLKNODEMETHOD(clknode_set_gate,         jz4780_clk_gen_set_gate),
   69         CLKNODEMETHOD(clknode_recalc_freq,      jz4780_clk_gen_recalc_freq),
   70         CLKNODEMETHOD(clknode_set_freq,         jz4780_clk_gen_set_freq),
   71         CLKNODEMETHOD(clknode_set_mux,          jz4780_clk_gen_set_mux),
   72 
   73         CLKNODEMETHOD_END
   74 };
   75 DEFINE_CLASS_1(jz4780_clk_pll, jz4780_clk_gen_class, jz4780_clk_gen_methods,
   76        sizeof(struct jz4780_clk_gen_sc), clknode_class);
   77 
   78 static inline unsigned
   79 mux_to_reg(unsigned src, unsigned map)
   80 {
   81         unsigned ret, bit;
   82 
   83         bit = (1u << 3);
   84         for (ret = 0; bit; ret++, bit >>= 1) {
   85                 if (map & bit) {
   86                         if (src-- == 0)
   87                                 return (ret);
   88                 }
   89         }
   90         panic("mux_to_reg");
   91 }
   92 
   93 static inline unsigned
   94 reg_to_mux(unsigned reg, unsigned map)
   95 {
   96         unsigned ret, bit;
   97 
   98         bit = (1u << 3);
   99         for (ret = 0; reg; reg--, bit >>= 1)
  100                 if (map & bit)
  101                         ret++;
  102         return (ret);
  103 }
  104 
  105 static int
  106 jz4780_clk_gen_init(struct clknode *clk, device_t dev)
  107 {
  108         struct jz4780_clk_gen_sc *sc;
  109         uint32_t reg, msk, parent_idx;
  110 
  111         sc = clknode_get_softc(clk);
  112         CLK_LOCK(sc);
  113         /* Figure our parent out */
  114         if (sc->clk_descr->clk_type & CLK_MASK_MUX) {
  115                 msk = (1u << sc->clk_descr->clk_mux.mux_bits) - 1;
  116                 reg = CLK_RD_4(sc, sc->clk_descr->clk_mux.mux_reg);
  117                 reg = (reg >> sc->clk_descr->clk_mux.mux_shift) & msk;
  118                 parent_idx = reg_to_mux(reg, sc->clk_descr->clk_mux.mux_map);
  119         } else
  120                 parent_idx = 0;
  121         CLK_UNLOCK(sc);
  122 
  123         clknode_init_parent_idx(clk, parent_idx);
  124         return (0);
  125 }
  126 
  127 static int
  128 jz4780_clk_gen_recalc_freq(struct clknode *clk, uint64_t *freq)
  129 {
  130         struct jz4780_clk_gen_sc *sc;
  131         uint32_t reg;
  132 
  133         sc = clknode_get_softc(clk);
  134 
  135         /* Calculate divisor frequency */
  136         if (sc->clk_descr->clk_type & CLK_MASK_DIV) {
  137                 uint32_t msk;
  138 
  139                 msk = (1u << sc->clk_descr->clk_div.div_bits) - 1;
  140                 reg = CLK_RD_4(sc, sc->clk_descr->clk_div.div_reg);
  141                 reg = (reg >> sc->clk_descr->clk_div.div_shift) & msk;
  142                 reg = (reg + 1) << sc->clk_descr->clk_div.div_lg;
  143                 *freq /= reg;
  144         }
  145         return (0);
  146 }
  147 
  148 #define DIV_TIMEOUT     100
  149 
  150 static int
  151 jz4780_clk_gen_set_freq(struct clknode *clk, uint64_t fin,
  152     uint64_t *fout, int flags, int *stop)
  153 {
  154         struct jz4780_clk_gen_sc *sc;
  155         uint64_t _fout;
  156         uint32_t divider, div_reg, div_msk, reg, div_l, div_h;
  157         int rv;
  158 
  159         sc = clknode_get_softc(clk);
  160 
  161         /* Find closest divider */
  162         div_l = howmany(fin, *fout);
  163         div_h = fin / *fout;
  164         divider = abs((int64_t)*fout - (fin / div_l)) <
  165             abs((int64_t)*fout - (fin / div_h)) ? div_l : div_h;
  166 
  167         /* Adjust for divider multiplier */
  168         div_reg = divider >> sc->clk_descr->clk_div.div_lg;
  169         divider = div_reg << sc->clk_descr->clk_div.div_lg;
  170         if (divider == 0)
  171                 divider = 1;
  172 
  173         _fout = fin / divider;
  174 
  175         /* Rounding */
  176         if ((flags & CLK_SET_ROUND_UP) && (*fout > _fout))
  177                 div_reg--;
  178         else if ((flags & CLK_SET_ROUND_DOWN) && (*fout < _fout))
  179                 div_reg++;
  180         if (div_reg == 0)
  181                 div_reg = 1;
  182 
  183         div_msk = (1u << sc->clk_descr->clk_div.div_bits) - 1;
  184 
  185         *stop = 1;
  186         if (div_reg > div_msk + 1) {
  187                 *stop = 0;
  188                 div_reg = div_msk;
  189         }
  190 
  191         divider = (div_reg << sc->clk_descr->clk_div.div_lg);
  192         div_reg--;
  193 
  194         if ((flags & CLK_SET_DRYRUN) != 0) {
  195                 if (*stop != 0 && *fout != fin / divider &&
  196                     (flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0)
  197                         return (ERANGE);
  198                 *fout = fin / divider;
  199                 return (0);
  200         }
  201 
  202         CLK_LOCK(sc);
  203         /* Apply the new divider value */
  204         reg = CLK_RD_4(sc, sc->clk_descr->clk_div.div_reg);
  205         reg &= ~(div_msk << sc->clk_descr->clk_div.div_shift);
  206         reg |= (div_reg << sc->clk_descr->clk_div.div_shift);
  207         /* Set the change enable bit, it present */
  208         if (sc->clk_descr->clk_div.div_ce_bit >= 0)
  209                 reg |= (1u << sc->clk_descr->clk_div.div_ce_bit);
  210         /* Clear stop bit, it present */
  211         if (sc->clk_descr->clk_div.div_st_bit >= 0)
  212                 reg &= ~(1u << sc->clk_descr->clk_div.div_st_bit);
  213         /* Initiate the change */
  214         CLK_WR_4(sc, sc->clk_descr->clk_div.div_reg, reg);
  215 
  216         /* Wait for busy bit to clear indicating the change is complete */
  217         rv = 0;
  218         if (sc->clk_descr->clk_div.div_busy_bit >= 0) {
  219                 int i;
  220 
  221                 for (i = 0;  i < DIV_TIMEOUT; i++) {
  222                         reg = CLK_RD_4(sc, sc->clk_descr->clk_div.div_reg);
  223                         if (!(reg & (1u << sc->clk_descr->clk_div.div_busy_bit)))
  224                                 break;
  225                         DELAY(1000);
  226                 }
  227                 if (i == DIV_TIMEOUT)
  228                         rv = ETIMEDOUT;
  229         }
  230         CLK_UNLOCK(sc);
  231 
  232         *fout = fin / divider;
  233         return (rv);
  234 }
  235 
  236 static int
  237 jz4780_clk_gen_set_mux(struct clknode *clk, int src)
  238 {
  239         struct jz4780_clk_gen_sc *sc;
  240         uint32_t reg, msk;
  241 
  242         sc = clknode_get_softc(clk);
  243 
  244         /* Only mux nodes are capable of being reparented */
  245         if (!(sc->clk_descr->clk_type & CLK_MASK_MUX))
  246                 return (src ? EINVAL : 0);
  247 
  248         msk = (1u << sc->clk_descr->clk_mux.mux_bits) - 1;
  249         src = mux_to_reg(src & msk, sc->clk_descr->clk_mux.mux_map);
  250 
  251         CLK_LOCK(sc);
  252         reg = CLK_RD_4(sc, sc->clk_descr->clk_mux.mux_reg);
  253         reg &= ~(msk << sc->clk_descr->clk_mux.mux_shift);
  254         reg |=  (src << sc->clk_descr->clk_mux.mux_shift);
  255         CLK_WR_4(sc, sc->clk_descr->clk_mux.mux_reg, reg);
  256         CLK_UNLOCK(sc);
  257 
  258         return (0);
  259 }
  260 
  261 static int
  262 jz4780_clk_gen_set_gate(struct clknode *clk, bool enable)
  263 {
  264         struct jz4780_clk_gen_sc *sc;
  265         uint32_t off, reg, bit;
  266 
  267         sc = clknode_get_softc(clk);
  268 
  269         /* Check is clock can be gated */
  270         if (sc->clk_descr->clk_gate_bit < 0)
  271                 return 0;
  272 
  273         bit = sc->clk_descr->clk_gate_bit;
  274         if (bit < 32) {
  275                 off = JZ_CLKGR0;
  276         } else {
  277                 off = JZ_CLKGR1;
  278                 bit -= 32;
  279         }
  280 
  281         CLK_LOCK(sc);
  282         reg = CLK_RD_4(sc, off);
  283         if (enable)
  284                 reg &= ~(1u << bit);
  285         else
  286                 reg |= (1u << bit);
  287         CLK_WR_4(sc, off, reg);
  288         CLK_UNLOCK(sc);
  289 
  290         return (0);
  291 }
  292 
  293 
  294 int jz4780_clk_gen_register(struct clkdom *clkdom,
  295     const struct jz4780_clk_descr *descr, struct mtx *dev_mtx,
  296     struct resource *mem_res)
  297 {
  298         struct clknode_init_def clkdef;
  299         struct clknode *clk;
  300         struct jz4780_clk_gen_sc *sc;
  301 
  302         clkdef.id = descr->clk_id;
  303         clkdef.name = __DECONST(char *, descr->clk_name);
  304         /* Silly const games to work around API deficiency */
  305         clkdef.parent_names = (const char **)(uintptr_t)&descr->clk_pnames[0];
  306         clkdef.flags = CLK_NODE_STATIC_STRINGS;
  307         if (descr->clk_type & CLK_MASK_MUX)
  308                 clkdef.parent_cnt = __bitcount16(descr->clk_mux.mux_map);
  309         else
  310                 clkdef.parent_cnt = 1;
  311 
  312         clk = clknode_create(clkdom, &jz4780_clk_gen_class, &clkdef);
  313         if (clk == NULL)
  314                 return (1);
  315 
  316         sc = clknode_get_softc(clk);
  317         sc->clk_mtx = dev_mtx;
  318         sc->clk_res = mem_res;
  319         sc->clk_descr = descr;
  320         clknode_register(clkdom, clk);
  321 
  322         return (0);
  323 }

Cache object: 1bfba61abdd928c65e0fed463078bdce


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