root/drivers/clk/qcom/clk-spmi-pmic-div.c

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

DEFINITIONS

This source file includes following definitions.
  1. to_clkdiv
  2. div_factor_to_div
  3. div_to_div_factor
  4. is_spmi_pmic_clkdiv_enabled
  5. __spmi_pmic_clkdiv_set_enable_state
  6. spmi_pmic_clkdiv_set_enable_state
  7. clk_spmi_pmic_div_enable
  8. clk_spmi_pmic_div_disable
  9. clk_spmi_pmic_div_round_rate
  10. clk_spmi_pmic_div_recalc_rate
  11. clk_spmi_pmic_div_set_rate
  12. spmi_pmic_div_clk_hw_get
  13. spmi_pmic_clkdiv_probe

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /* Copyright (c) 2017, The Linux Foundation. All rights reserved.
   3  */
   4 
   5 #include <linux/bitops.h>
   6 #include <linux/clk.h>
   7 #include <linux/clk-provider.h>
   8 #include <linux/delay.h>
   9 #include <linux/err.h>
  10 #include <linux/log2.h>
  11 #include <linux/module.h>
  12 #include <linux/of.h>
  13 #include <linux/platform_device.h>
  14 #include <linux/regmap.h>
  15 #include <linux/slab.h>
  16 #include <linux/types.h>
  17 
  18 #define REG_DIV_CTL1                    0x43
  19 #define DIV_CTL1_DIV_FACTOR_MASK        GENMASK(2, 0)
  20 
  21 #define REG_EN_CTL                      0x46
  22 #define REG_EN_MASK                     BIT(7)
  23 
  24 struct clkdiv {
  25         struct regmap           *regmap;
  26         u16                     base;
  27         spinlock_t              lock;
  28 
  29         struct clk_hw           hw;
  30         unsigned int            cxo_period_ns;
  31 };
  32 
  33 static inline struct clkdiv *to_clkdiv(struct clk_hw *hw)
  34 {
  35         return container_of(hw, struct clkdiv, hw);
  36 }
  37 
  38 static inline unsigned int div_factor_to_div(unsigned int div_factor)
  39 {
  40         if (!div_factor)
  41                 div_factor = 1;
  42 
  43         return 1 << (div_factor - 1);
  44 }
  45 
  46 static inline unsigned int div_to_div_factor(unsigned int div)
  47 {
  48         return min(ilog2(div) + 1, 7);
  49 }
  50 
  51 static bool is_spmi_pmic_clkdiv_enabled(struct clkdiv *clkdiv)
  52 {
  53         unsigned int val = 0;
  54 
  55         regmap_read(clkdiv->regmap, clkdiv->base + REG_EN_CTL, &val);
  56 
  57         return val & REG_EN_MASK;
  58 }
  59 
  60 static int
  61 __spmi_pmic_clkdiv_set_enable_state(struct clkdiv *clkdiv, bool enable,
  62                                     unsigned int div_factor)
  63 {
  64         int ret;
  65         unsigned int ns = clkdiv->cxo_period_ns;
  66         unsigned int div = div_factor_to_div(div_factor);
  67 
  68         ret = regmap_update_bits(clkdiv->regmap, clkdiv->base + REG_EN_CTL,
  69                                  REG_EN_MASK, enable ? REG_EN_MASK : 0);
  70         if (ret)
  71                 return ret;
  72 
  73         if (enable)
  74                 ndelay((2 + 3 * div) * ns);
  75         else
  76                 ndelay(3 * div * ns);
  77 
  78         return 0;
  79 }
  80 
  81 static int spmi_pmic_clkdiv_set_enable_state(struct clkdiv *clkdiv, bool enable)
  82 {
  83         unsigned int div_factor;
  84 
  85         regmap_read(clkdiv->regmap, clkdiv->base + REG_DIV_CTL1, &div_factor);
  86         div_factor &= DIV_CTL1_DIV_FACTOR_MASK;
  87 
  88         return __spmi_pmic_clkdiv_set_enable_state(clkdiv, enable, div_factor);
  89 }
  90 
  91 static int clk_spmi_pmic_div_enable(struct clk_hw *hw)
  92 {
  93         struct clkdiv *clkdiv = to_clkdiv(hw);
  94         unsigned long flags;
  95         int ret;
  96 
  97         spin_lock_irqsave(&clkdiv->lock, flags);
  98         ret = spmi_pmic_clkdiv_set_enable_state(clkdiv, true);
  99         spin_unlock_irqrestore(&clkdiv->lock, flags);
 100 
 101         return ret;
 102 }
 103 
 104 static void clk_spmi_pmic_div_disable(struct clk_hw *hw)
 105 {
 106         struct clkdiv *clkdiv = to_clkdiv(hw);
 107         unsigned long flags;
 108 
 109         spin_lock_irqsave(&clkdiv->lock, flags);
 110         spmi_pmic_clkdiv_set_enable_state(clkdiv, false);
 111         spin_unlock_irqrestore(&clkdiv->lock, flags);
 112 }
 113 
 114 static long clk_spmi_pmic_div_round_rate(struct clk_hw *hw, unsigned long rate,
 115                                          unsigned long *parent_rate)
 116 {
 117         unsigned int div, div_factor;
 118 
 119         div = DIV_ROUND_UP(*parent_rate, rate);
 120         div_factor = div_to_div_factor(div);
 121         div = div_factor_to_div(div_factor);
 122 
 123         return *parent_rate / div;
 124 }
 125 
 126 static unsigned long
 127 clk_spmi_pmic_div_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
 128 {
 129         struct clkdiv *clkdiv = to_clkdiv(hw);
 130         unsigned int div_factor;
 131 
 132         regmap_read(clkdiv->regmap, clkdiv->base + REG_DIV_CTL1, &div_factor);
 133         div_factor &= DIV_CTL1_DIV_FACTOR_MASK;
 134 
 135         return parent_rate / div_factor_to_div(div_factor);
 136 }
 137 
 138 static int clk_spmi_pmic_div_set_rate(struct clk_hw *hw, unsigned long rate,
 139                                       unsigned long parent_rate)
 140 {
 141         struct clkdiv *clkdiv = to_clkdiv(hw);
 142         unsigned int div_factor = div_to_div_factor(parent_rate / rate);
 143         unsigned long flags;
 144         bool enabled;
 145         int ret;
 146 
 147         spin_lock_irqsave(&clkdiv->lock, flags);
 148         enabled = is_spmi_pmic_clkdiv_enabled(clkdiv);
 149         if (enabled) {
 150                 ret = spmi_pmic_clkdiv_set_enable_state(clkdiv, false);
 151                 if (ret)
 152                         goto unlock;
 153         }
 154 
 155         ret = regmap_update_bits(clkdiv->regmap, clkdiv->base + REG_DIV_CTL1,
 156                                  DIV_CTL1_DIV_FACTOR_MASK, div_factor);
 157         if (ret)
 158                 goto unlock;
 159 
 160         if (enabled)
 161                 ret = __spmi_pmic_clkdiv_set_enable_state(clkdiv, true,
 162                                                           div_factor);
 163 
 164 unlock:
 165         spin_unlock_irqrestore(&clkdiv->lock, flags);
 166 
 167         return ret;
 168 }
 169 
 170 static const struct clk_ops clk_spmi_pmic_div_ops = {
 171         .enable = clk_spmi_pmic_div_enable,
 172         .disable = clk_spmi_pmic_div_disable,
 173         .set_rate = clk_spmi_pmic_div_set_rate,
 174         .recalc_rate = clk_spmi_pmic_div_recalc_rate,
 175         .round_rate = clk_spmi_pmic_div_round_rate,
 176 };
 177 
 178 struct spmi_pmic_div_clk_cc {
 179         int             nclks;
 180         struct clkdiv   clks[];
 181 };
 182 
 183 static struct clk_hw *
 184 spmi_pmic_div_clk_hw_get(struct of_phandle_args *clkspec, void *data)
 185 {
 186         struct spmi_pmic_div_clk_cc *cc = data;
 187         int idx = clkspec->args[0] - 1; /* Start at 1 instead of 0 */
 188 
 189         if (idx < 0 || idx >= cc->nclks) {
 190                 pr_err("%s: index value %u is invalid; allowed range [1, %d]\n",
 191                        __func__, clkspec->args[0], cc->nclks);
 192                 return ERR_PTR(-EINVAL);
 193         }
 194 
 195         return &cc->clks[idx].hw;
 196 }
 197 
 198 static int spmi_pmic_clkdiv_probe(struct platform_device *pdev)
 199 {
 200         struct spmi_pmic_div_clk_cc *cc;
 201         struct clk_init_data init = {};
 202         struct clkdiv *clkdiv;
 203         struct clk *cxo;
 204         struct regmap *regmap;
 205         struct device *dev = &pdev->dev;
 206         struct device_node *of_node = dev->of_node;
 207         const char *parent_name;
 208         int nclks, i, ret, cxo_hz;
 209         char name[20];
 210         u32 start;
 211 
 212         ret = of_property_read_u32(of_node, "reg", &start);
 213         if (ret < 0) {
 214                 dev_err(dev, "reg property reading failed\n");
 215                 return ret;
 216         }
 217 
 218         regmap = dev_get_regmap(dev->parent, NULL);
 219         if (!regmap) {
 220                 dev_err(dev, "Couldn't get parent's regmap\n");
 221                 return -EINVAL;
 222         }
 223 
 224         ret = of_property_read_u32(of_node, "qcom,num-clkdivs", &nclks);
 225         if (ret < 0) {
 226                 dev_err(dev, "qcom,num-clkdivs property reading failed, ret=%d\n",
 227                         ret);
 228                 return ret;
 229         }
 230 
 231         if (!nclks)
 232                 return -EINVAL;
 233 
 234         cc = devm_kzalloc(dev, struct_size(cc, clks, nclks), GFP_KERNEL);
 235         if (!cc)
 236                 return -ENOMEM;
 237         cc->nclks = nclks;
 238 
 239         cxo = clk_get(dev, "xo");
 240         if (IS_ERR(cxo)) {
 241                 ret = PTR_ERR(cxo);
 242                 if (ret != -EPROBE_DEFER)
 243                         dev_err(dev, "failed to get xo clock\n");
 244                 return ret;
 245         }
 246         cxo_hz = clk_get_rate(cxo);
 247         clk_put(cxo);
 248 
 249         parent_name = of_clk_get_parent_name(of_node, 0);
 250         if (!parent_name) {
 251                 dev_err(dev, "missing parent clock\n");
 252                 return -ENODEV;
 253         }
 254 
 255         init.name = name;
 256         init.parent_names = &parent_name;
 257         init.num_parents = 1;
 258         init.ops = &clk_spmi_pmic_div_ops;
 259 
 260         for (i = 0, clkdiv = cc->clks; i < nclks; i++) {
 261                 snprintf(name, sizeof(name), "div_clk%d", i + 1);
 262 
 263                 spin_lock_init(&clkdiv[i].lock);
 264                 clkdiv[i].base = start + i * 0x100;
 265                 clkdiv[i].regmap = regmap;
 266                 clkdiv[i].cxo_period_ns = NSEC_PER_SEC / cxo_hz;
 267                 clkdiv[i].hw.init = &init;
 268 
 269                 ret = devm_clk_hw_register(dev, &clkdiv[i].hw);
 270                 if (ret)
 271                         return ret;
 272         }
 273 
 274         return devm_of_clk_add_hw_provider(dev, spmi_pmic_div_clk_hw_get, cc);
 275 }
 276 
 277 static const struct of_device_id spmi_pmic_clkdiv_match_table[] = {
 278         { .compatible = "qcom,spmi-clkdiv" },
 279         { /* sentinel */ }
 280 };
 281 MODULE_DEVICE_TABLE(of, spmi_pmic_clkdiv_match_table);
 282 
 283 static struct platform_driver spmi_pmic_clkdiv_driver = {
 284         .driver         = {
 285                 .name   = "qcom,spmi-pmic-clkdiv",
 286                 .of_match_table = spmi_pmic_clkdiv_match_table,
 287         },
 288         .probe          = spmi_pmic_clkdiv_probe,
 289 };
 290 module_platform_driver(spmi_pmic_clkdiv_driver);
 291 
 292 MODULE_DESCRIPTION("QCOM SPMI PMIC clkdiv driver");
 293 MODULE_LICENSE("GPL v2");

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