root/drivers/clk/imx/clk-divider-gate.c

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

DEFINITIONS

This source file includes following definitions.
  1. to_clk_divider_gate
  2. clk_divider_gate_recalc_rate_ro
  3. clk_divider_gate_recalc_rate
  4. clk_divider_round_rate
  5. clk_divider_gate_set_rate
  6. clk_divider_enable
  7. clk_divider_disable
  8. clk_divider_is_enabled
  9. imx_clk_divider_gate

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  * Copyright 2018 NXP.
   4  *   Dong Aisheng <aisheng.dong@nxp.com>
   5  */
   6 
   7 #include <linux/clk-provider.h>
   8 #include <linux/err.h>
   9 #include <linux/io.h>
  10 #include <linux/slab.h>
  11 
  12 #include "clk.h"
  13 
  14 struct clk_divider_gate {
  15         struct clk_divider divider;
  16         u32 cached_val;
  17 };
  18 
  19 static inline struct clk_divider_gate *to_clk_divider_gate(struct clk_hw *hw)
  20 {
  21         struct clk_divider *div = to_clk_divider(hw);
  22 
  23         return container_of(div, struct clk_divider_gate, divider);
  24 }
  25 
  26 static unsigned long clk_divider_gate_recalc_rate_ro(struct clk_hw *hw,
  27                                                      unsigned long parent_rate)
  28 {
  29         struct clk_divider *div = to_clk_divider(hw);
  30         unsigned int val;
  31 
  32         val = readl(div->reg) >> div->shift;
  33         val &= clk_div_mask(div->width);
  34         if (!val)
  35                 return 0;
  36 
  37         return divider_recalc_rate(hw, parent_rate, val, div->table,
  38                                    div->flags, div->width);
  39 }
  40 
  41 static unsigned long clk_divider_gate_recalc_rate(struct clk_hw *hw,
  42                                                   unsigned long parent_rate)
  43 {
  44         struct clk_divider_gate *div_gate = to_clk_divider_gate(hw);
  45         struct clk_divider *div = to_clk_divider(hw);
  46         unsigned long flags = 0;
  47         unsigned int val;
  48 
  49         spin_lock_irqsave(div->lock, flags);
  50 
  51         if (!clk_hw_is_enabled(hw)) {
  52                 val = div_gate->cached_val;
  53         } else {
  54                 val = readl(div->reg) >> div->shift;
  55                 val &= clk_div_mask(div->width);
  56         }
  57 
  58         spin_unlock_irqrestore(div->lock, flags);
  59 
  60         if (!val)
  61                 return 0;
  62 
  63         return divider_recalc_rate(hw, parent_rate, val, div->table,
  64                                    div->flags, div->width);
  65 }
  66 
  67 static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
  68                                    unsigned long *prate)
  69 {
  70         return clk_divider_ops.round_rate(hw, rate, prate);
  71 }
  72 
  73 static int clk_divider_gate_set_rate(struct clk_hw *hw, unsigned long rate,
  74                                 unsigned long parent_rate)
  75 {
  76         struct clk_divider_gate *div_gate = to_clk_divider_gate(hw);
  77         struct clk_divider *div = to_clk_divider(hw);
  78         unsigned long flags = 0;
  79         int value;
  80         u32 val;
  81 
  82         value = divider_get_val(rate, parent_rate, div->table,
  83                                 div->width, div->flags);
  84         if (value < 0)
  85                 return value;
  86 
  87         spin_lock_irqsave(div->lock, flags);
  88 
  89         if (clk_hw_is_enabled(hw)) {
  90                 val = readl(div->reg);
  91                 val &= ~(clk_div_mask(div->width) << div->shift);
  92                 val |= (u32)value << div->shift;
  93                 writel(val, div->reg);
  94         } else {
  95                 div_gate->cached_val = value;
  96         }
  97 
  98         spin_unlock_irqrestore(div->lock, flags);
  99 
 100         return 0;
 101 }
 102 
 103 static int clk_divider_enable(struct clk_hw *hw)
 104 {
 105         struct clk_divider_gate *div_gate = to_clk_divider_gate(hw);
 106         struct clk_divider *div = to_clk_divider(hw);
 107         unsigned long flags = 0;
 108         u32 val;
 109 
 110         if (!div_gate->cached_val) {
 111                 pr_err("%s: no valid preset rate\n", clk_hw_get_name(hw));
 112                 return -EINVAL;
 113         }
 114 
 115         spin_lock_irqsave(div->lock, flags);
 116         /* restore div val */
 117         val = readl(div->reg);
 118         val |= div_gate->cached_val << div->shift;
 119         writel(val, div->reg);
 120 
 121         spin_unlock_irqrestore(div->lock, flags);
 122 
 123         return 0;
 124 }
 125 
 126 static void clk_divider_disable(struct clk_hw *hw)
 127 {
 128         struct clk_divider_gate *div_gate = to_clk_divider_gate(hw);
 129         struct clk_divider *div = to_clk_divider(hw);
 130         unsigned long flags = 0;
 131         u32 val;
 132 
 133         spin_lock_irqsave(div->lock, flags);
 134 
 135         /* store the current div val */
 136         val = readl(div->reg) >> div->shift;
 137         val &= clk_div_mask(div->width);
 138         div_gate->cached_val = val;
 139         writel(0, div->reg);
 140 
 141         spin_unlock_irqrestore(div->lock, flags);
 142 }
 143 
 144 static int clk_divider_is_enabled(struct clk_hw *hw)
 145 {
 146         struct clk_divider *div = to_clk_divider(hw);
 147         u32 val;
 148 
 149         val = readl(div->reg) >> div->shift;
 150         val &= clk_div_mask(div->width);
 151 
 152         return val ? 1 : 0;
 153 }
 154 
 155 static const struct clk_ops clk_divider_gate_ro_ops = {
 156         .recalc_rate = clk_divider_gate_recalc_rate_ro,
 157         .round_rate = clk_divider_round_rate,
 158 };
 159 
 160 static const struct clk_ops clk_divider_gate_ops = {
 161         .recalc_rate = clk_divider_gate_recalc_rate,
 162         .round_rate = clk_divider_round_rate,
 163         .set_rate = clk_divider_gate_set_rate,
 164         .enable = clk_divider_enable,
 165         .disable = clk_divider_disable,
 166         .is_enabled = clk_divider_is_enabled,
 167 };
 168 
 169 /*
 170  * NOTE: In order to resue the most code from the common divider,
 171  * we also design our divider following the way that provids an extra
 172  * clk_divider_flags, however it's fixed to CLK_DIVIDER_ONE_BASED by
 173  * default as our HW is. Besides that it supports only CLK_DIVIDER_READ_ONLY
 174  * flag which can be specified by user flexibly.
 175  */
 176 struct clk_hw *imx_clk_divider_gate(const char *name, const char *parent_name,
 177                                     unsigned long flags, void __iomem *reg,
 178                                     u8 shift, u8 width, u8 clk_divider_flags,
 179                                     const struct clk_div_table *table,
 180                                     spinlock_t *lock)
 181 {
 182         struct clk_init_data init;
 183         struct clk_divider_gate *div_gate;
 184         struct clk_hw *hw;
 185         u32 val;
 186         int ret;
 187 
 188         div_gate  = kzalloc(sizeof(*div_gate), GFP_KERNEL);
 189         if (!div_gate)
 190                 return ERR_PTR(-ENOMEM);
 191 
 192         init.name = name;
 193         if (clk_divider_flags & CLK_DIVIDER_READ_ONLY)
 194                 init.ops = &clk_divider_gate_ro_ops;
 195         else
 196                 init.ops = &clk_divider_gate_ops;
 197         init.flags = flags;
 198         init.parent_names = parent_name ? &parent_name : NULL;
 199         init.num_parents = parent_name ? 1 : 0;
 200 
 201         div_gate->divider.reg = reg;
 202         div_gate->divider.shift = shift;
 203         div_gate->divider.width = width;
 204         div_gate->divider.lock = lock;
 205         div_gate->divider.table = table;
 206         div_gate->divider.hw.init = &init;
 207         div_gate->divider.flags = CLK_DIVIDER_ONE_BASED | clk_divider_flags;
 208         /* cache gate status */
 209         val = readl(reg) >> shift;
 210         val &= clk_div_mask(width);
 211         div_gate->cached_val = val;
 212 
 213         hw = &div_gate->divider.hw;
 214         ret = clk_hw_register(NULL, hw);
 215         if (ret) {
 216                 kfree(div_gate);
 217                 hw = ERR_PTR(ret);
 218         }
 219 
 220         return hw;
 221 }

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