root/drivers/clk/imx/clk-composite-8m.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. imx8m_clk_composite_divider_recalc_rate
  2. imx8m_clk_composite_compute_dividers
  3. imx8m_clk_composite_divider_round_rate
  4. imx8m_clk_composite_divider_set_rate
  5. imx8m_clk_composite_flags

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Copyright 2018 NXP
   4  */
   5 
   6 #include <linux/clk-provider.h>
   7 #include <linux/errno.h>
   8 #include <linux/io.h>
   9 #include <linux/slab.h>
  10 
  11 #include "clk.h"
  12 
  13 #define PCG_PREDIV_SHIFT        16
  14 #define PCG_PREDIV_WIDTH        3
  15 #define PCG_PREDIV_MAX          8
  16 
  17 #define PCG_DIV_SHIFT           0
  18 #define PCG_DIV_WIDTH           6
  19 #define PCG_DIV_MAX             64
  20 
  21 #define PCG_PCS_SHIFT           24
  22 #define PCG_PCS_MASK            0x7
  23 
  24 #define PCG_CGC_SHIFT           28
  25 
  26 static unsigned long imx8m_clk_composite_divider_recalc_rate(struct clk_hw *hw,
  27                                                 unsigned long parent_rate)
  28 {
  29         struct clk_divider *divider = to_clk_divider(hw);
  30         unsigned long prediv_rate;
  31         unsigned int prediv_value;
  32         unsigned int div_value;
  33 
  34         prediv_value = readl(divider->reg) >> divider->shift;
  35         prediv_value &= clk_div_mask(divider->width);
  36 
  37         prediv_rate = divider_recalc_rate(hw, parent_rate, prediv_value,
  38                                                 NULL, divider->flags,
  39                                                 divider->width);
  40 
  41         div_value = readl(divider->reg) >> PCG_DIV_SHIFT;
  42         div_value &= clk_div_mask(PCG_DIV_WIDTH);
  43 
  44         return divider_recalc_rate(hw, prediv_rate, div_value, NULL,
  45                                    divider->flags, PCG_DIV_WIDTH);
  46 }
  47 
  48 static int imx8m_clk_composite_compute_dividers(unsigned long rate,
  49                                                 unsigned long parent_rate,
  50                                                 int *prediv, int *postdiv)
  51 {
  52         int div1, div2;
  53         int error = INT_MAX;
  54         int ret = -EINVAL;
  55 
  56         *prediv = 1;
  57         *postdiv = 1;
  58 
  59         for (div1 = 1; div1 <= PCG_PREDIV_MAX; div1++) {
  60                 for (div2 = 1; div2 <= PCG_DIV_MAX; div2++) {
  61                         int new_error = ((parent_rate / div1) / div2) - rate;
  62 
  63                         if (abs(new_error) < abs(error)) {
  64                                 *prediv = div1;
  65                                 *postdiv = div2;
  66                                 error = new_error;
  67                                 ret = 0;
  68                         }
  69                 }
  70         }
  71         return ret;
  72 }
  73 
  74 static long imx8m_clk_composite_divider_round_rate(struct clk_hw *hw,
  75                                                 unsigned long rate,
  76                                                 unsigned long *prate)
  77 {
  78         int prediv_value;
  79         int div_value;
  80 
  81         imx8m_clk_composite_compute_dividers(rate, *prate,
  82                                                 &prediv_value, &div_value);
  83         rate = DIV_ROUND_UP(*prate, prediv_value);
  84 
  85         return DIV_ROUND_UP(rate, div_value);
  86 
  87 }
  88 
  89 static int imx8m_clk_composite_divider_set_rate(struct clk_hw *hw,
  90                                         unsigned long rate,
  91                                         unsigned long parent_rate)
  92 {
  93         struct clk_divider *divider = to_clk_divider(hw);
  94         unsigned long flags = 0;
  95         int prediv_value;
  96         int div_value;
  97         int ret;
  98         u32 val;
  99 
 100         ret = imx8m_clk_composite_compute_dividers(rate, parent_rate,
 101                                                 &prediv_value, &div_value);
 102         if (ret)
 103                 return -EINVAL;
 104 
 105         spin_lock_irqsave(divider->lock, flags);
 106 
 107         val = readl(divider->reg);
 108         val &= ~((clk_div_mask(divider->width) << divider->shift) |
 109                         (clk_div_mask(PCG_DIV_WIDTH) << PCG_DIV_SHIFT));
 110 
 111         val |= (u32)(prediv_value  - 1) << divider->shift;
 112         val |= (u32)(div_value - 1) << PCG_DIV_SHIFT;
 113         writel(val, divider->reg);
 114 
 115         spin_unlock_irqrestore(divider->lock, flags);
 116 
 117         return ret;
 118 }
 119 
 120 static const struct clk_ops imx8m_clk_composite_divider_ops = {
 121         .recalc_rate = imx8m_clk_composite_divider_recalc_rate,
 122         .round_rate = imx8m_clk_composite_divider_round_rate,
 123         .set_rate = imx8m_clk_composite_divider_set_rate,
 124 };
 125 
 126 struct clk *imx8m_clk_composite_flags(const char *name,
 127                                         const char * const *parent_names,
 128                                         int num_parents, void __iomem *reg,
 129                                         unsigned long flags)
 130 {
 131         struct clk_hw *hw = ERR_PTR(-ENOMEM), *mux_hw;
 132         struct clk_hw *div_hw, *gate_hw;
 133         struct clk_divider *div = NULL;
 134         struct clk_gate *gate = NULL;
 135         struct clk_mux *mux = NULL;
 136 
 137         mux = kzalloc(sizeof(*mux), GFP_KERNEL);
 138         if (!mux)
 139                 goto fail;
 140 
 141         mux_hw = &mux->hw;
 142         mux->reg = reg;
 143         mux->shift = PCG_PCS_SHIFT;
 144         mux->mask = PCG_PCS_MASK;
 145         mux->lock = &imx_ccm_lock;
 146 
 147         div = kzalloc(sizeof(*div), GFP_KERNEL);
 148         if (!div)
 149                 goto fail;
 150 
 151         div_hw = &div->hw;
 152         div->reg = reg;
 153         div->shift = PCG_PREDIV_SHIFT;
 154         div->width = PCG_PREDIV_WIDTH;
 155         div->lock = &imx_ccm_lock;
 156         div->flags = CLK_DIVIDER_ROUND_CLOSEST;
 157 
 158         gate = kzalloc(sizeof(*gate), GFP_KERNEL);
 159         if (!gate)
 160                 goto fail;
 161 
 162         gate_hw = &gate->hw;
 163         gate->reg = reg;
 164         gate->bit_idx = PCG_CGC_SHIFT;
 165         gate->lock = &imx_ccm_lock;
 166 
 167         hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
 168                         mux_hw, &clk_mux_ops, div_hw,
 169                         &imx8m_clk_composite_divider_ops,
 170                         gate_hw, &clk_gate_ops, flags);
 171         if (IS_ERR(hw))
 172                 goto fail;
 173 
 174         return hw->clk;
 175 
 176 fail:
 177         kfree(gate);
 178         kfree(div);
 179         kfree(mux);
 180         return ERR_CAST(hw);
 181 }

/* [<][>][^][v][top][bottom][index][help] */