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/qcom_clk/qcom_clk_branch2.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) 2021 Adrian Chadd <adrian@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 AND CONTRIBUTORS ``AS IS'' AND
   14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   21  * LIABILITY, 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 
   26 #include <sys/cdefs.h>
   27 __FBSDID("$FreeBSD$");
   28 
   29 #include <sys/param.h>
   30 #include <sys/systm.h>
   31 #include <sys/bus.h>
   32 #include <sys/lock.h>
   33 #include <sys/mutex.h>
   34 #include <sys/rman.h>
   35 #include <machine/bus.h>
   36 
   37 #include <dev/extres/clk/clk.h>
   38 #include <dev/extres/clk/clk_div.h>
   39 #include <dev/extres/clk/clk_fixed.h>
   40 #include <dev/extres/clk/clk_mux.h>
   41 
   42 #include "qcom_clk_branch2.h"
   43 #include "qcom_clk_branch2_reg.h"
   44 
   45 #include "clkdev_if.h"
   46 
   47 /*
   48  * This is a combination gate/status and dynamic hardware clock gating with
   49  * voting.
   50  */
   51 
   52 #if 0
   53 #define DPRINTF(dev, msg...) device_printf(dev, msg);
   54 #else
   55 #define DPRINTF(dev, msg...)
   56 #endif
   57 
   58 struct qcom_clk_branch2_sc {
   59         struct clknode *clknode;
   60         uint32_t flags;
   61         uint32_t enable_offset;
   62         uint32_t enable_shift;
   63         uint32_t hwcg_reg;
   64         uint32_t hwcg_bit;
   65         uint32_t halt_reg;
   66         uint32_t halt_check_type;
   67         bool halt_check_voted;
   68 };
   69 #if 0
   70 static bool
   71 qcom_clk_branch2_get_gate_locked(struct qcom_clk_branch2_sc *sc)
   72 {
   73         uint32_t reg;
   74 
   75         CLKDEV_READ_4(clknode_get_device(sc->clknode), sc->enable_offset,
   76             &reg);
   77 
   78         DPRINTF(clknode_get_device(sc->clknode),
   79             "%s: offset=0x%x, reg=0x%x\n", __func__,
   80             sc->enable_offset, reg);
   81 
   82         return (!! (reg & (1U << sc->enable_shift)));
   83 }
   84 #endif
   85 
   86 static int
   87 qcom_clk_branch2_init(struct clknode *clk, device_t dev)
   88 {
   89 
   90         clknode_init_parent_idx(clk, 0);
   91 
   92         return (0);
   93 }
   94 
   95 static bool
   96 qcom_clk_branch2_in_hwcg_mode_locked(struct qcom_clk_branch2_sc *sc)
   97 {
   98         uint32_t reg;
   99 
  100         if (sc->hwcg_reg == 0)
  101                 return (false);
  102         
  103         CLKDEV_READ_4(clknode_get_device(sc->clknode), sc->hwcg_reg,
  104             &reg);
  105 
  106         return (!! (reg & (1U << sc->hwcg_bit)));
  107 }
  108 
  109 static bool
  110 qcom_clk_branch2_check_halt_locked(struct qcom_clk_branch2_sc *sc, bool enable)
  111 {
  112         uint32_t reg;
  113 
  114         CLKDEV_READ_4(clknode_get_device(sc->clknode), sc->halt_reg, &reg);
  115 
  116         if (enable) {
  117                 /*
  118                  * The upstream Linux code is .. unfortunate.
  119                  *
  120                  * Here it says "return true if BRANCH_CLK_OFF is not set,
  121                  * or if the status field = FSM_STATUS_ON AND
  122                  * the clk_off field is 0.
  123                  *
  124                  * Which .. is weird, because I can't currently see
  125                  * how we'd ever need to check FSM_STATUS_ON - the only
  126                  * valid check for the FSM status also requires clk_off=0.
  127                  */
  128                 return !! ((reg & QCOM_CLK_BRANCH2_CLK_OFF) == 0);
  129         } else {
  130                 return !! (reg & QCOM_CLK_BRANCH2_CLK_OFF);
  131         }
  132 }
  133 
  134 /*
  135  * Check if the given type/voted flag match what is configured.
  136  */
  137 static bool
  138 qcom_clk_branch2_halt_check_type(struct qcom_clk_branch2_sc *sc,
  139     uint32_t type, bool voted)
  140 {
  141         return ((sc->halt_check_type == type) &&
  142             (sc->halt_check_voted == voted));
  143 }
  144 
  145 static bool
  146 qcom_clk_branch2_wait_locked(struct qcom_clk_branch2_sc *sc, bool enable)
  147 {
  148 
  149         if (qcom_clk_branch2_halt_check_type(sc,
  150             QCOM_CLK_BRANCH2_BRANCH_HALT_SKIP, false))
  151                 return (true);
  152         if (qcom_clk_branch2_in_hwcg_mode_locked(sc))
  153                 return (true);
  154 
  155         if ((qcom_clk_branch2_halt_check_type(sc,
  156               QCOM_CLK_BRANCH2_BRANCH_HALT_DELAY, false)) ||
  157             (enable == false && sc->halt_check_voted)) {
  158                 DELAY(10);
  159                 return (true);
  160         }
  161 
  162         if ((qcom_clk_branch2_halt_check_type(sc,
  163               QCOM_CLK_BRANCH2_BRANCH_HALT_INVERTED, false)) ||
  164             (qcom_clk_branch2_halt_check_type(sc,
  165               QCOM_CLK_BRANCH2_BRANCH_HALT, false)) ||
  166             (enable && sc->halt_check_voted)) {
  167                 int count;
  168 
  169                 for (count = 0; count < 200; count++) {
  170                         if (qcom_clk_branch2_check_halt_locked(sc, enable))
  171                                 return (true);
  172                         DELAY(1);
  173                 }
  174                 DPRINTF(clknode_get_device(sc->clknode),
  175                     "%s: enable stuck (%d)!\n", __func__, enable);
  176                 return (false);
  177         }
  178 
  179         /* Default */
  180         return (true);
  181 }
  182 
  183 static int
  184 qcom_clk_branch2_set_gate(struct clknode *clk, bool enable)
  185 {
  186         struct qcom_clk_branch2_sc *sc;
  187         uint32_t reg;
  188 
  189         sc = clknode_get_softc(clk);
  190 
  191         DPRINTF(clknode_get_device(sc->clknode), "%s: called\n", __func__);
  192 
  193         if (sc->enable_offset == 0) {
  194                 DPRINTF(clknode_get_device(sc->clknode),
  195                     "%s: no enable_offset", __func__);
  196                 return (ENXIO);
  197         }
  198 
  199         DPRINTF(clknode_get_device(sc->clknode),
  200             "%s: called; enable=%d\n", __func__, enable);
  201 
  202         CLKDEV_DEVICE_LOCK(clknode_get_device(sc->clknode));
  203         CLKDEV_READ_4(clknode_get_device(sc->clknode), sc->enable_offset,
  204             &reg);
  205         if (enable) {
  206                 reg |= (1U << sc->enable_shift);
  207         } else {
  208                 reg &= ~(1U << sc->enable_shift);
  209         }
  210         CLKDEV_WRITE_4(clknode_get_device(sc->clknode), sc->enable_offset,
  211             reg);
  212 
  213         /*
  214          * Now wait for the clock branch to update!
  215          */
  216         if (! qcom_clk_branch2_wait_locked(sc, enable)) {
  217                 CLKDEV_DEVICE_UNLOCK(clknode_get_device(sc->clknode));
  218                 DPRINTF(clknode_get_device(sc->clknode),
  219                     "%s: failed to wait!\n", __func__);
  220                 return (ENXIO);
  221         }
  222 
  223         CLKDEV_DEVICE_UNLOCK(clknode_get_device(sc->clknode));
  224 
  225         return (0);
  226 }
  227 
  228 static int
  229 qcom_clk_branch2_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
  230     int flags, int *stop)
  231 {
  232         struct qcom_clk_branch2_sc *sc;
  233 
  234         sc = clknode_get_softc(clk);
  235 
  236         /* We only support what our parent clock is currently set as */
  237         *fout = fin;
  238 
  239         /* .. and stop here if we don't have SET_RATE_PARENT */
  240         if (sc->flags & QCOM_CLK_BRANCH2_FLAGS_SET_RATE_PARENT)
  241                 *stop = 0;
  242         else
  243                 *stop = 1;
  244         return (0);
  245 }
  246 
  247 
  248 static clknode_method_t qcom_clk_branch2_methods[] = {
  249         /* Device interface */
  250         CLKNODEMETHOD(clknode_init,             qcom_clk_branch2_init),
  251         CLKNODEMETHOD(clknode_set_gate,         qcom_clk_branch2_set_gate),
  252         CLKNODEMETHOD(clknode_set_freq,         qcom_clk_branch2_set_freq),
  253         CLKNODEMETHOD_END
  254 };
  255 
  256 DEFINE_CLASS_1(qcom_clk_branch2, qcom_clk_branch2_class,
  257     qcom_clk_branch2_methods, sizeof(struct qcom_clk_branch2_sc),
  258     clknode_class);
  259 
  260 int
  261 qcom_clk_branch2_register(struct clkdom *clkdom,
  262     struct qcom_clk_branch2_def *clkdef)
  263 {
  264         struct clknode *clk;
  265         struct qcom_clk_branch2_sc *sc;
  266 
  267         if (clkdef->flags & QCOM_CLK_BRANCH2_FLAGS_CRITICAL)
  268                 clkdef->clkdef.flags |= CLK_NODE_CANNOT_STOP;
  269 
  270         clk = clknode_create(clkdom, &qcom_clk_branch2_class,
  271             &clkdef->clkdef);
  272         if (clk == NULL)
  273                 return (1);
  274 
  275         sc = clknode_get_softc(clk);
  276         sc->clknode = clk;
  277 
  278         sc->enable_offset = clkdef->enable_offset;
  279         sc->enable_shift = clkdef->enable_shift;
  280         sc->halt_reg = clkdef->halt_reg;
  281         sc->hwcg_reg = clkdef->hwcg_reg;
  282         sc->hwcg_bit = clkdef->hwcg_bit;
  283         sc->halt_check_type = clkdef->halt_check_type;
  284         sc->halt_check_voted = clkdef->halt_check_voted;
  285         sc->flags = clkdef->flags;
  286 
  287         clknode_register(clkdom, clk);
  288 
  289         return (0);
  290 }

Cache object: b22bd2ae51fd2679dd26f82f1fe98ee0


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