clk-spmi-pmic-div.c (7044B)
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 24struct 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 33static inline struct clkdiv *to_clkdiv(struct clk_hw *hw) 34{ 35 return container_of(hw, struct clkdiv, hw); 36} 37 38static 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 46static inline unsigned int div_to_div_factor(unsigned int div) 47{ 48 return min(ilog2(div) + 1, 7); 49} 50 51static 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 60static 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 81static 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 91static 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 104static 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 114static 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 126static unsigned long 127clk_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 138static 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 164unlock: 165 spin_unlock_irqrestore(&clkdiv->lock, flags); 166 167 return ret; 168} 169 170static 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 178struct spmi_pmic_div_clk_cc { 179 int nclks; 180 struct clkdiv clks[]; 181}; 182 183static struct clk_hw * 184spmi_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 198static 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 277static const struct of_device_id spmi_pmic_clkdiv_match_table[] = { 278 { .compatible = "qcom,spmi-clkdiv" }, 279 { /* sentinel */ } 280}; 281MODULE_DEVICE_TABLE(of, spmi_pmic_clkdiv_match_table); 282 283static 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}; 290module_platform_driver(spmi_pmic_clkdiv_driver); 291 292MODULE_DESCRIPTION("QCOM SPMI PMIC clkdiv driver"); 293MODULE_LICENSE("GPL v2");