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_rcg2.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_freqtbl.h"
   43 #include "qcom_clk_rcg2.h"
   44 #include "qcom_clk_rcg2_reg.h"
   45 
   46 #include "clkdev_if.h"
   47 
   48 #if 0
   49 #define DPRINTF(dev, msg...) device_printf(dev, msg);
   50 #else
   51 #define DPRINTF(dev, msg...)
   52 #endif
   53 
   54 #define QCOM_CLK_RCG2_CFG_OFFSET(sc)    \
   55             ((sc)->cmd_rcgr + (sc)->cfg_offset + QCOM_CLK_RCG2_CFG_REG)
   56 #define QCOM_CLK_RCG2_CMD_REGISTER(sc)  \
   57             ((sc)->cmd_rcgr + QCOM_CLK_RCG2_CMD_REG)
   58 #define QCOM_CLK_RCG2_M_OFFSET(sc)      \
   59             ((sc)->cmd_rcgr + (sc)->cfg_offset + QCOM_CLK_RCG2_M_REG)
   60 #define QCOM_CLK_RCG2_N_OFFSET(sc)      \
   61             ((sc)->cmd_rcgr + (sc)->cfg_offset + QCOM_CLK_RCG2_N_REG)
   62 #define QCOM_CLK_RCG2_D_OFFSET(sc)      \
   63             ((sc)->cmd_rcgr + (sc)->cfg_offset + QCOM_CLK_RCG2_D_REG)
   64 
   65 struct qcom_clk_rcg2_sc {
   66         struct clknode *clknode;
   67         uint32_t cmd_rcgr;
   68         uint32_t hid_width;
   69         uint32_t mnd_width;
   70         int32_t safe_src_idx;
   71         uint32_t cfg_offset;
   72         int safe_pre_parent_idx;
   73         uint32_t flags;
   74         const struct qcom_clk_freq_tbl *freq_tbl;
   75 };
   76 
   77 
   78 /*
   79  * Finish a clock update.
   80  *
   81  * This instructs the configuration to take effect.
   82  */
   83 static bool
   84 qcom_clk_rcg2_update_config_locked(struct qcom_clk_rcg2_sc *sc)
   85 {
   86         uint32_t reg, count;
   87 
   88         /*
   89          * Send "update" to the controller.
   90          */
   91         CLKDEV_READ_4(clknode_get_device(sc->clknode),
   92             QCOM_CLK_RCG2_CMD_REGISTER(sc), &reg);
   93         reg |= QCOM_CLK_RCG2_CMD_UPDATE;
   94         CLKDEV_WRITE_4(clknode_get_device(sc->clknode),
   95             QCOM_CLK_RCG2_CMD_REGISTER(sc), reg);
   96         wmb();
   97 
   98         /*
   99          * Poll for completion of update.
  100          */
  101         for (count = 0; count < 1000; count++) {
  102                 CLKDEV_READ_4(clknode_get_device(sc->clknode),
  103                     QCOM_CLK_RCG2_CMD_REGISTER(sc), &reg);
  104                 if ((reg & QCOM_CLK_RCG2_CMD_UPDATE) == 0) {
  105                         return (true);
  106                 }
  107                 DELAY(10);
  108                 rmb();
  109         }
  110 
  111         CLKDEV_READ_4(clknode_get_device(sc->clknode),
  112             QCOM_CLK_RCG2_CMD_REGISTER(sc), &reg);
  113         DPRINTF(clknode_get_device(sc->clknode), "%s: failed; reg=0x%08x\n",
  114             __func__, reg);
  115         return (false);
  116 }
  117 
  118 /*
  119  * Calculate the output frequency given an input frequency and the m/n:d
  120  * configuration.
  121  */
  122 static uint64_t
  123 qcom_clk_rcg2_calc_rate(uint64_t rate, uint32_t mode, uint32_t m, uint32_t n,
  124     uint32_t hid_div)
  125 {
  126         if (hid_div != 0) {
  127                 rate = rate * 2;
  128                 rate = rate / (hid_div + 1);
  129         }
  130 
  131         /* Note: assume n is not 0 here; bad things happen if it is */
  132 
  133         if (mode != 0) {
  134                 rate = (rate * m) / n;
  135         }
  136 
  137         return (rate);
  138 }
  139 
  140 /*
  141  * The inverse of calc_rate() - calculate the required input frequency
  142  * given the desired output freqency and m/n:d configuration.
  143  */
  144 static uint64_t
  145 qcom_clk_rcg2_calc_input_freq(uint64_t freq, uint32_t m, uint32_t n,
  146     uint32_t hid_div)
  147 {
  148         if (hid_div != 0) {
  149                 freq = freq / 2;
  150                 freq = freq * (hid_div + 1);
  151         }
  152 
  153         if (n != 0) {
  154                 freq = (freq * n) / m;
  155         }
  156 
  157         return (freq);
  158 }
  159 
  160 static int
  161 qcom_clk_rcg2_recalc(struct clknode *clk, uint64_t *freq)
  162 {
  163         struct qcom_clk_rcg2_sc *sc;
  164         uint32_t cfg, m = 0, n = 0, hid_div = 0;
  165         uint32_t mode = 0, mask;
  166 
  167         sc = clknode_get_softc(clk);
  168 
  169         /* Read the MODE, CFG, M and N parameters */
  170         CLKDEV_DEVICE_LOCK(clknode_get_device(sc->clknode));
  171         CLKDEV_READ_4(clknode_get_device(sc->clknode),
  172             QCOM_CLK_RCG2_CFG_OFFSET(sc),
  173             &cfg);
  174         if (sc->mnd_width != 0) {
  175                 mask = (1U << sc->mnd_width) - 1;
  176                 CLKDEV_READ_4(clknode_get_device(sc->clknode),
  177                     QCOM_CLK_RCG2_M_OFFSET(sc), &m);
  178                 CLKDEV_READ_4(clknode_get_device(sc->clknode),
  179                     QCOM_CLK_RCG2_N_OFFSET(sc), &n);
  180                 m = m & mask;
  181                 n = ~ n;
  182                 n = n & mask;
  183                 n = n + m;
  184                 mode = (cfg & QCOM_CLK_RCG2_CFG_MODE_MASK)
  185                     >> QCOM_CLK_RCG2_CFG_MODE_SHIFT;
  186         }
  187         CLKDEV_DEVICE_UNLOCK(clknode_get_device(sc->clknode));
  188 
  189         /* Fetch the divisor */
  190         mask = (1U << sc->hid_width) - 1;
  191         hid_div = (cfg >> QCOM_CLK_RCG2_CFG_SRC_DIV_SHIFT) & mask;
  192 
  193         /* Calculate the rate based on the parent rate and config */
  194         *freq = qcom_clk_rcg2_calc_rate(*freq, mode, m, n, hid_div);
  195 
  196         return (0);
  197 }
  198 
  199 /*
  200  * configure the mn:d divisor, pre-divisor, and parent.
  201  */
  202 static void
  203 qcom_clk_rcg2_set_config_locked(struct qcom_clk_rcg2_sc *sc,
  204     const struct qcom_clk_freq_tbl *f, int parent_idx)
  205 {
  206         uint32_t mask, reg;
  207 
  208         /* If we have MN:D, then update it */
  209         if (sc->mnd_width != 0 && f->n != 0) {
  210                 mask = (1U << sc->mnd_width) - 1;
  211 
  212                 CLKDEV_READ_4(clknode_get_device(sc->clknode),
  213                     QCOM_CLK_RCG2_M_OFFSET(sc), &reg);
  214                 reg &= ~mask;
  215                 reg |= (f->m & mask);
  216                 CLKDEV_WRITE_4(clknode_get_device(sc->clknode),
  217                     QCOM_CLK_RCG2_M_OFFSET(sc), reg);
  218 
  219                 CLKDEV_READ_4(clknode_get_device(sc->clknode),
  220                     QCOM_CLK_RCG2_N_OFFSET(sc), &reg);
  221                 reg &= ~mask;
  222                 reg |= ((~(f->n - f->m)) & mask);
  223                 CLKDEV_WRITE_4(clknode_get_device(sc->clknode),
  224                     QCOM_CLK_RCG2_N_OFFSET(sc), reg);
  225 
  226                 CLKDEV_READ_4(clknode_get_device(sc->clknode),
  227                     QCOM_CLK_RCG2_D_OFFSET(sc), &reg);
  228                 reg &= ~mask;
  229                 reg |= ((~f->n) & mask);
  230                 CLKDEV_WRITE_4(clknode_get_device(sc->clknode),
  231                     QCOM_CLK_RCG2_D_OFFSET(sc), reg);
  232         }
  233 
  234         mask = (1U << sc->hid_width) - 1;
  235         /*
  236          * Mask out register fields we're going to modify along with
  237          * the pre-divisor.
  238          */
  239         mask |= QCOM_CLK_RCG2_CFG_SRC_SEL_MASK
  240             | QCOM_CLK_RCG2_CFG_MODE_MASK
  241             | QCOM_CLK_RCG2_CFG_HW_CLK_CTRL_MASK;
  242 
  243         CLKDEV_READ_4(clknode_get_device(sc->clknode),
  244             QCOM_CLK_RCG2_CFG_OFFSET(sc), &reg);
  245         reg &= ~mask;
  246 
  247         /* Configure pre-divisor */
  248         reg = reg | ((f->pre_div) << QCOM_CLK_RCG2_CFG_SRC_DIV_SHIFT);
  249 
  250         /* Configure parent clock */
  251         reg = reg | (((parent_idx << QCOM_CLK_RCG2_CFG_SRC_SEL_SHIFT)
  252             & QCOM_CLK_RCG2_CFG_SRC_SEL_MASK));
  253 
  254         /* Configure dual-edge if needed */
  255         if (sc->mnd_width != 0 && f->n != 0 && (f->m != f->n))
  256                 reg |= QCOM_CLK_RCG2_CFG_MODE_DUAL_EDGE;
  257 
  258         CLKDEV_WRITE_4(clknode_get_device(sc->clknode),
  259             QCOM_CLK_RCG2_CFG_OFFSET(sc), reg);
  260 }
  261 
  262 static int
  263 qcom_clk_rcg2_init(struct clknode *clk, device_t dev)
  264 {
  265         struct qcom_clk_rcg2_sc *sc;
  266         uint32_t reg;
  267         uint32_t idx;
  268         bool enabled __unused;
  269 
  270         sc = clknode_get_softc(clk);
  271 
  272         /*
  273          * Read the mux setting to set the right parent.
  274          * Whilst here, read the config to get whether we're enabled
  275          * or not.
  276          */
  277         CLKDEV_DEVICE_LOCK(clknode_get_device(sc->clknode));
  278         /* check if rcg2 root clock is enabled */
  279         CLKDEV_READ_4(clknode_get_device(sc->clknode),
  280             QCOM_CLK_RCG2_CMD_REGISTER(sc), &reg);
  281         if (reg & QCOM_CLK_RCG2_CMD_ROOT_OFF)
  282                 enabled = false;
  283         else
  284                 enabled = true;
  285 
  286         /* mux settings */
  287         CLKDEV_READ_4(clknode_get_device(sc->clknode),
  288             QCOM_CLK_RCG2_CFG_OFFSET(sc), &reg);
  289         CLKDEV_DEVICE_UNLOCK(clknode_get_device(sc->clknode));
  290 
  291         idx = (reg & QCOM_CLK_RCG2_CFG_SRC_SEL_MASK)
  292             >> QCOM_CLK_RCG2_CFG_SRC_SEL_SHIFT;
  293         DPRINTF(clknode_get_device(sc->clknode),
  294             "%s: mux index %u, enabled=%d\n",
  295             __func__, idx, enabled);
  296         clknode_init_parent_idx(clk, idx);
  297 
  298         /*
  299          * If we could be sure our parent clocks existed here in the tree,
  300          * we could calculate our current frequency by fetching the parent
  301          * frequency and then do our divider math.  Unfortunately that
  302          * currently isn't the case.
  303          */
  304 
  305         return(0);
  306 }
  307 
  308 static int
  309 qcom_clk_rcg2_set_gate(struct clknode *clk, bool enable)
  310 {
  311 
  312         /*
  313          * For now this isn't supported; there's some support for
  314          * "shared" rcg2 nodes in the Qualcomm/upstream Linux trees but
  315          * it's not currently needed for the supported platforms.
  316          */
  317         return (0);
  318 }
  319 
  320 /*
  321  * Program the parent index.
  322  *
  323  * This doesn't do the update.  It also must be called with the device
  324  * lock held.
  325  */
  326 static void
  327 qcom_clk_rcg2_set_parent_index_locked(struct qcom_clk_rcg2_sc *sc,
  328     uint32_t index)
  329 {
  330         uint32_t reg;
  331 
  332         CLKDEV_READ_4(clknode_get_device(sc->clknode),
  333             QCOM_CLK_RCG2_CFG_OFFSET(sc), &reg);
  334         reg = reg & ~QCOM_CLK_RCG2_CFG_SRC_SEL_MASK;
  335         reg = reg | (((index << QCOM_CLK_RCG2_CFG_SRC_SEL_SHIFT)
  336             & QCOM_CLK_RCG2_CFG_SRC_SEL_MASK));
  337         CLKDEV_WRITE_4(clknode_get_device(sc->clknode),
  338             QCOM_CLK_RCG2_CFG_OFFSET(sc),
  339             reg);
  340 }
  341 
  342 /*
  343  * Set frequency
  344  *
  345  * fin - the parent frequency, if exists
  346  * fout - starts as the requested frequency, ends with the configured
  347  *        or dry-run frequency
  348  * Flags - CLK_SET_DRYRUN, CLK_SET_ROUND_UP, CLK_SET_ROUND_DOWN
  349  * retval - 0, ERANGE
  350  */
  351 static int
  352 qcom_clk_rcg2_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
  353     int flags, int *stop)
  354 {
  355         struct qcom_clk_rcg2_sc *sc;
  356         const struct qcom_clk_freq_tbl *f;
  357         const char **parent_names;
  358         uint64_t p_freq, p_clk_freq;
  359         int parent_cnt;
  360         struct clknode *p_clk;
  361         int i;
  362 
  363         sc = clknode_get_softc(clk);
  364 
  365         /*
  366          * Find a suitable frequency in the frequency table.
  367          *
  368          * TODO: should pay attention to ROUND_UP / ROUND_DOWN and add
  369          * a freqtbl method to handle both accordingly.
  370          */
  371         f = qcom_clk_freq_tbl_lookup(sc->freq_tbl, *fout);
  372         if (f == NULL) {
  373                 device_printf(clknode_get_device(sc->clknode),
  374                     "%s: no suitable freqtbl entry found for freq %llu\n",
  375                     __func__,
  376                     *fout);
  377                 return (ERANGE);
  378         }
  379 
  380         /*
  381          * Find the parent index for the given parent clock.
  382          * Abort if we can't actually find it.
  383          *
  384          * XXX TODO: this should be a clk API call!
  385          */
  386         parent_cnt = clknode_get_parents_num(clk);
  387         parent_names = clknode_get_parent_names(clk);
  388         for (i = 0; i < parent_cnt; i++) {
  389                 if (parent_names[i] == NULL)
  390                         continue;
  391                 if (strcmp(parent_names[i], f->parent) == 0)
  392                         break;
  393         }
  394         if (i >= parent_cnt) {
  395                 device_printf(clknode_get_device(sc->clknode),
  396                     "%s: couldn't find suitable parent?\n",
  397                     __func__);
  398                 return (ENXIO);
  399         }
  400 
  401         /*
  402          * If we aren't setting the parent clock, then we need
  403          * to just program the new parent clock in and update.
  404          * (or for DRYRUN just skip that and return the new
  405          * frequency.)
  406          */
  407         if ((sc->flags & QCOM_CLK_RCG2_FLAGS_SET_RATE_PARENT) == 0) {
  408                 if (flags & CLK_SET_DRYRUN) {
  409                         *fout = f->freq;
  410                         return (0);
  411                 }
  412 
  413                 if (sc->safe_pre_parent_idx > -1) {
  414                         DPRINTF(clknode_get_device(sc->clknode),
  415                             "%s: setting to safe parent idx %d\n",
  416                             __func__,
  417                             sc->safe_pre_parent_idx);
  418                         CLKDEV_DEVICE_LOCK(clknode_get_device(sc->clknode));
  419                         qcom_clk_rcg2_set_parent_index_locked(sc,
  420                             sc->safe_pre_parent_idx);
  421                         DPRINTF(clknode_get_device(sc->clknode),
  422                             "%s: safe parent: updating config\n", __func__);
  423                         if (! qcom_clk_rcg2_update_config_locked(sc)) {
  424                                 CLKDEV_DEVICE_UNLOCK(clknode_get_device(sc->clknode));
  425                                 DPRINTF(clknode_get_device(sc->clknode),
  426                                     "%s: error updating config\n",
  427                                     __func__);
  428                                 return (ENXIO);
  429                         }
  430                         CLKDEV_DEVICE_UNLOCK(clknode_get_device(sc->clknode));
  431                         DPRINTF(clknode_get_device(sc->clknode),
  432                             "%s: safe parent: done\n", __func__);
  433                         clknode_set_parent_by_idx(sc->clknode,
  434                             sc->safe_pre_parent_idx);
  435                 }
  436                 /* Program parent index, then schedule update */
  437                 CLKDEV_DEVICE_LOCK(clknode_get_device(sc->clknode));
  438                 qcom_clk_rcg2_set_parent_index_locked(sc, i);
  439                 if (! qcom_clk_rcg2_update_config_locked(sc)) {
  440                         CLKDEV_DEVICE_UNLOCK(clknode_get_device(sc->clknode));
  441                         device_printf(clknode_get_device(sc->clknode),
  442                             "%s: couldn't program in parent idx %u!\n",
  443                             __func__, i);
  444                         return (ENXIO);
  445                 }
  446                 CLKDEV_DEVICE_UNLOCK(clknode_get_device(sc->clknode));
  447                 clknode_set_parent_by_idx(sc->clknode, i);
  448                 *fout = f->freq;
  449                 return (0);
  450         }
  451 
  452         /*
  453          * If we /are/ setting the parent clock, then we need
  454          * to determine what frequency we need the parent to
  455          * be, and then reconfigure the parent to the new
  456          * frequency, and then change our parent.
  457          *
  458          * (Again, if we're doing DRYRUN, just skip that
  459          * and return the new frequency.)
  460          */
  461         p_clk = clknode_find_by_name(f->parent);
  462         if (p_clk == NULL) {
  463                 device_printf(clknode_get_device(sc->clknode),
  464                     "%s: couldn't find parent clk (%s)\n",
  465                     __func__, f->parent);
  466                 return (ENXIO);
  467         }
  468 
  469         /*
  470          * Calculate required frequency from said parent clock to
  471          * meet the needs of our target clock.
  472          */
  473         p_freq = qcom_clk_rcg2_calc_input_freq(f->freq, f->m, f->n,
  474             f->pre_div);
  475         DPRINTF(clknode_get_device(sc->clknode),
  476             "%s: request %llu, parent %s freq %llu, parent freq %llu\n",
  477             __func__,
  478             *fout,
  479             f->parent,
  480             f->freq,
  481             p_freq);
  482 
  483         /*
  484          * To ensure glitch-free operation on some clocks, set it to
  485          * a safe parent before programming our divisor and the parent
  486          * clock configuration.  Then once it's done, flip the parent
  487          * to the new parent.
  488          *
  489          * If we're doing a dry-run then we don't need to re-parent the
  490          * clock just yet!
  491          */
  492         if (((flags & CLK_SET_DRYRUN) == 0) &&
  493             (sc->safe_pre_parent_idx > -1)) {
  494                 DPRINTF(clknode_get_device(sc->clknode),
  495                     "%s: setting to safe parent idx %d\n",
  496                     __func__,
  497                     sc->safe_pre_parent_idx);
  498                 CLKDEV_DEVICE_LOCK(clknode_get_device(sc->clknode));
  499                 qcom_clk_rcg2_set_parent_index_locked(sc,
  500                     sc->safe_pre_parent_idx);
  501                 DPRINTF(clknode_get_device(sc->clknode),
  502                     "%s: safe parent: updating config\n", __func__);
  503                 if (! qcom_clk_rcg2_update_config_locked(sc)) {
  504                         CLKDEV_DEVICE_UNLOCK(clknode_get_device(sc->clknode));
  505                         DPRINTF(clknode_get_device(sc->clknode),
  506                             "%s: error updating config\n",
  507                             __func__);
  508                         return (ENXIO);
  509                 }
  510                 CLKDEV_DEVICE_UNLOCK(clknode_get_device(sc->clknode));
  511                 DPRINTF(clknode_get_device(sc->clknode),
  512                     "%s: safe parent: done\n", __func__);
  513                 clknode_set_parent_by_idx(sc->clknode,
  514                     sc->safe_pre_parent_idx);
  515         }
  516 
  517         /*
  518          * Set the parent frequency before we change our mux and divisor
  519          * configuration.
  520          */
  521         if (clknode_get_freq(p_clk, &p_clk_freq) != 0) {
  522                 device_printf(clknode_get_device(sc->clknode),
  523                     "%s: couldn't get freq for parent clock %s\n",
  524                     __func__,
  525                     f->parent);
  526                 return (ENXIO);
  527         }
  528         if (p_clk_freq != p_freq) {
  529                 uint64_t n_freq;
  530                 int rv;
  531 
  532                 /*
  533                  * If we're doing a dryrun then call test_freq() not set_freq().
  534                  * That way we get the frequency back that we would be set to.
  535                  *
  536                  * If we're not doing a dry run then set the frequency, then
  537                  * call get_freq to get what it was set to.
  538                  */
  539                 if (flags & CLK_SET_DRYRUN) {
  540                         n_freq = p_freq;
  541                         rv = clknode_test_freq(p_clk, n_freq, flags, 0,
  542                             &p_freq);
  543                 } else {
  544                         rv = clknode_set_freq(p_clk, p_freq, flags, 0);
  545                 }
  546 
  547                 if (rv != 0) {
  548                         device_printf(clknode_get_device(sc->clknode),
  549                             "%s: couldn't set parent clock %s frequency to "
  550                             "%llu\n",
  551                             __func__,
  552                             f->parent,
  553                             p_freq);
  554                         return (ENXIO);
  555                 }
  556 
  557                 /* Frequency was set, fetch what it was set to */
  558                 if ((flags & CLK_SET_DRYRUN) == 0) {
  559                         rv = clknode_get_freq(p_clk, &p_freq);
  560                         if (rv != 0) {
  561                                 device_printf(clknode_get_device(sc->clknode),
  562                                     "%s: couldn't get parent frequency",
  563                                     __func__);
  564                                 return (ENXIO);
  565                         }
  566                 }
  567         }
  568 
  569         DPRINTF(clknode_get_device(sc->clknode),
  570             "%s: requested freq=%llu, target freq=%llu,"
  571             " parent choice=%s, parent_freq=%llu\n",
  572             __func__,
  573             *fout,
  574             f->freq,
  575             f->parent,
  576             p_freq);
  577 
  578         /*
  579          * Set the parent node, the parent programming and the divisor
  580          * config.  Because they're done together, we don't go via
  581          * a mux method on this node.
  582          */
  583 
  584         /*
  585          * Program the divisor and parent.
  586          */
  587         if ((flags & CLK_SET_DRYRUN) == 0) {
  588                 CLKDEV_DEVICE_LOCK(clknode_get_device(sc->clknode));
  589                 qcom_clk_rcg2_set_config_locked(sc, f, i);
  590                 if (! qcom_clk_rcg2_update_config_locked(sc)) {
  591                         CLKDEV_DEVICE_UNLOCK(clknode_get_device(sc->clknode));
  592                         device_printf(clknode_get_device(sc->clknode),
  593                             "%s: couldn't program in divisor, help!\n",
  594                             __func__);
  595                         return (ENXIO);
  596                 }
  597                 CLKDEV_DEVICE_UNLOCK(clknode_get_device(sc->clknode));
  598                 clknode_set_parent_by_idx(sc->clknode, i);
  599         }
  600 
  601         /*
  602          * p_freq is now the frequency that the parent /is/ set to.
  603          * (Or would be set to for a dry run.)
  604          *
  605          * Calculate what the eventual frequency would be, we'll want
  606          * this to return when we're done - and again, if it's a dryrun,
  607          * don't set anything up.  This doesn't rely on the register
  608          * contents.
  609          */
  610         *fout = qcom_clk_rcg2_calc_rate(p_freq, (f->n == 0 ? 0 : 1),
  611             f->m, f->n, f->pre_div);
  612 
  613         return (0);
  614 }
  615 
  616 static clknode_method_t qcom_clk_rcg2_methods[] = {
  617         /* Device interface */
  618         CLKNODEMETHOD(clknode_init,             qcom_clk_rcg2_init),
  619         CLKNODEMETHOD(clknode_recalc_freq,      qcom_clk_rcg2_recalc),
  620         CLKNODEMETHOD(clknode_set_gate,         qcom_clk_rcg2_set_gate),
  621         CLKNODEMETHOD(clknode_set_freq,         qcom_clk_rcg2_set_freq),
  622         CLKNODEMETHOD_END
  623 };
  624 
  625 DEFINE_CLASS_1(qcom_clk_fepll, qcom_clk_rcg2_class, qcom_clk_rcg2_methods,
  626    sizeof(struct qcom_clk_rcg2_sc), clknode_class);
  627 
  628 int
  629 qcom_clk_rcg2_register(struct clkdom *clkdom,
  630     struct qcom_clk_rcg2_def *clkdef)
  631 {
  632         struct clknode *clk;
  633         struct qcom_clk_rcg2_sc *sc;
  634 
  635         /*
  636          * Right now the rcg2 code isn't supporting turning off the clock
  637          * or limiting it to the lowest parent clock.  But, do set the
  638          * flags appropriately.
  639          */
  640         if (clkdef->flags & QCOM_CLK_RCG2_FLAGS_CRITICAL)
  641                 clkdef->clkdef.flags |= CLK_NODE_CANNOT_STOP;
  642 
  643         clk = clknode_create(clkdom, &qcom_clk_rcg2_class, &clkdef->clkdef);
  644         if (clk == NULL)
  645                 return (1);
  646 
  647         sc = clknode_get_softc(clk);
  648         sc->clknode = clk;
  649 
  650         sc->cmd_rcgr = clkdef->cmd_rcgr;
  651         sc->hid_width = clkdef->hid_width;
  652         sc->mnd_width = clkdef->mnd_width;
  653         sc->safe_src_idx = clkdef->safe_src_idx;
  654         sc->safe_pre_parent_idx = clkdef->safe_pre_parent_idx;
  655         sc->cfg_offset = clkdef->cfg_offset;
  656         sc->flags = clkdef->flags;
  657         sc->freq_tbl = clkdef->freq_tbl;
  658 
  659         clknode_register(clkdom, clk);
  660 
  661         return (0);
  662 }

Cache object: a9ccafbe2fe7ec63ba9bdec6040b00e9


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