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