1 /* 2 * OMAP APLL clock support 3 * 4 * Copyright (C) 2013 Texas Instruments, Inc. 5 * 6 * J Keerthy <j-keerthy@ti.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 * 12 * This program is distributed "as is" WITHOUT ANY WARRANTY of any 13 * kind, whether express or implied; without even the implied warranty 14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 */ 17 18 #include <linux/clk-provider.h> 19 #include <linux/module.h> 20 #include <linux/slab.h> 21 #include <linux/io.h> 22 #include <linux/err.h> 23 #include <linux/string.h> 24 #include <linux/log2.h> 25 #include <linux/of.h> 26 #include <linux/of_address.h> 27 #include <linux/clk/ti.h> 28 #include <linux/delay.h> 29 30 #define APLL_FORCE_LOCK 0x1 31 #define APLL_AUTO_IDLE 0x2 32 #define MAX_APLL_WAIT_TRIES 1000000 33 34 #undef pr_fmt 35 #define pr_fmt(fmt) "%s: " fmt, __func__ 36 37 static int dra7_apll_enable(struct clk_hw *hw) 38 { 39 struct clk_hw_omap *clk = to_clk_hw_omap(hw); 40 int r = 0, i = 0; 41 struct dpll_data *ad; 42 const char *clk_name; 43 u8 state = 1; 44 u32 v; 45 46 ad = clk->dpll_data; 47 if (!ad) 48 return -EINVAL; 49 50 clk_name = __clk_get_name(clk->hw.clk); 51 52 state <<= __ffs(ad->idlest_mask); 53 54 /* Check is already locked */ 55 v = ti_clk_ll_ops->clk_readl(ad->idlest_reg); 56 57 if ((v & ad->idlest_mask) == state) 58 return r; 59 60 v = ti_clk_ll_ops->clk_readl(ad->control_reg); 61 v &= ~ad->enable_mask; 62 v |= APLL_FORCE_LOCK << __ffs(ad->enable_mask); 63 ti_clk_ll_ops->clk_writel(v, ad->control_reg); 64 65 state <<= __ffs(ad->idlest_mask); 66 67 while (1) { 68 v = ti_clk_ll_ops->clk_readl(ad->idlest_reg); 69 if ((v & ad->idlest_mask) == state) 70 break; 71 if (i > MAX_APLL_WAIT_TRIES) 72 break; 73 i++; 74 udelay(1); 75 } 76 77 if (i == MAX_APLL_WAIT_TRIES) { 78 pr_warn("clock: %s failed transition to '%s'\n", 79 clk_name, (state) ? "locked" : "bypassed"); 80 } else { 81 pr_debug("clock: %s transition to '%s' in %d loops\n", 82 clk_name, (state) ? "locked" : "bypassed", i); 83 84 r = 0; 85 } 86 87 return r; 88 } 89 90 static void dra7_apll_disable(struct clk_hw *hw) 91 { 92 struct clk_hw_omap *clk = to_clk_hw_omap(hw); 93 struct dpll_data *ad; 94 u8 state = 1; 95 u32 v; 96 97 ad = clk->dpll_data; 98 99 state <<= __ffs(ad->idlest_mask); 100 101 v = ti_clk_ll_ops->clk_readl(ad->control_reg); 102 v &= ~ad->enable_mask; 103 v |= APLL_AUTO_IDLE << __ffs(ad->enable_mask); 104 ti_clk_ll_ops->clk_writel(v, ad->control_reg); 105 } 106 107 static int dra7_apll_is_enabled(struct clk_hw *hw) 108 { 109 struct clk_hw_omap *clk = to_clk_hw_omap(hw); 110 struct dpll_data *ad; 111 u32 v; 112 113 ad = clk->dpll_data; 114 115 v = ti_clk_ll_ops->clk_readl(ad->control_reg); 116 v &= ad->enable_mask; 117 118 v >>= __ffs(ad->enable_mask); 119 120 return v == APLL_AUTO_IDLE ? 0 : 1; 121 } 122 123 static u8 dra7_init_apll_parent(struct clk_hw *hw) 124 { 125 return 0; 126 } 127 128 static const struct clk_ops apll_ck_ops = { 129 .enable = &dra7_apll_enable, 130 .disable = &dra7_apll_disable, 131 .is_enabled = &dra7_apll_is_enabled, 132 .get_parent = &dra7_init_apll_parent, 133 }; 134 135 static void __init omap_clk_register_apll(struct clk_hw *hw, 136 struct device_node *node) 137 { 138 struct clk_hw_omap *clk_hw = to_clk_hw_omap(hw); 139 struct dpll_data *ad = clk_hw->dpll_data; 140 struct clk *clk; 141 142 ad->clk_ref = of_clk_get(node, 0); 143 ad->clk_bypass = of_clk_get(node, 1); 144 145 if (IS_ERR(ad->clk_ref) || IS_ERR(ad->clk_bypass)) { 146 pr_debug("clk-ref or clk-bypass for %s not ready, retry\n", 147 node->name); 148 if (!ti_clk_retry_init(node, hw, omap_clk_register_apll)) 149 return; 150 151 goto cleanup; 152 } 153 154 clk = clk_register(NULL, &clk_hw->hw); 155 if (!IS_ERR(clk)) { 156 of_clk_add_provider(node, of_clk_src_simple_get, clk); 157 kfree(clk_hw->hw.init->parent_names); 158 kfree(clk_hw->hw.init); 159 return; 160 } 161 162 cleanup: 163 kfree(clk_hw->dpll_data); 164 kfree(clk_hw->hw.init->parent_names); 165 kfree(clk_hw->hw.init); 166 kfree(clk_hw); 167 } 168 169 static void __init of_dra7_apll_setup(struct device_node *node) 170 { 171 struct dpll_data *ad = NULL; 172 struct clk_hw_omap *clk_hw = NULL; 173 struct clk_init_data *init = NULL; 174 const char **parent_names = NULL; 175 int i; 176 177 ad = kzalloc(sizeof(*ad), GFP_KERNEL); 178 clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL); 179 init = kzalloc(sizeof(*init), GFP_KERNEL); 180 if (!ad || !clk_hw || !init) 181 goto cleanup; 182 183 clk_hw->dpll_data = ad; 184 clk_hw->hw.init = init; 185 clk_hw->flags = MEMMAP_ADDRESSING; 186 187 init->name = node->name; 188 init->ops = &apll_ck_ops; 189 190 init->num_parents = of_clk_get_parent_count(node); 191 if (init->num_parents < 1) { 192 pr_err("dra7 apll %s must have parent(s)\n", node->name); 193 goto cleanup; 194 } 195 196 parent_names = kzalloc(sizeof(char *) * init->num_parents, GFP_KERNEL); 197 if (!parent_names) 198 goto cleanup; 199 200 for (i = 0; i < init->num_parents; i++) 201 parent_names[i] = of_clk_get_parent_name(node, i); 202 203 init->parent_names = parent_names; 204 205 ad->control_reg = ti_clk_get_reg_addr(node, 0); 206 ad->idlest_reg = ti_clk_get_reg_addr(node, 1); 207 208 if (!ad->control_reg || !ad->idlest_reg) 209 goto cleanup; 210 211 ad->idlest_mask = 0x1; 212 ad->enable_mask = 0x3; 213 214 omap_clk_register_apll(&clk_hw->hw, node); 215 return; 216 217 cleanup: 218 kfree(parent_names); 219 kfree(ad); 220 kfree(clk_hw); 221 kfree(init); 222 } 223 CLK_OF_DECLARE(dra7_apll_clock, "ti,dra7-apll-clock", of_dra7_apll_setup); 224 225 #define OMAP2_EN_APLL_LOCKED 0x3 226 #define OMAP2_EN_APLL_STOPPED 0x0 227 228 static int omap2_apll_is_enabled(struct clk_hw *hw) 229 { 230 struct clk_hw_omap *clk = to_clk_hw_omap(hw); 231 struct dpll_data *ad = clk->dpll_data; 232 u32 v; 233 234 v = ti_clk_ll_ops->clk_readl(ad->control_reg); 235 v &= ad->enable_mask; 236 237 v >>= __ffs(ad->enable_mask); 238 239 return v == OMAP2_EN_APLL_LOCKED ? 1 : 0; 240 } 241 242 static unsigned long omap2_apll_recalc(struct clk_hw *hw, 243 unsigned long parent_rate) 244 { 245 struct clk_hw_omap *clk = to_clk_hw_omap(hw); 246 247 if (omap2_apll_is_enabled(hw)) 248 return clk->fixed_rate; 249 250 return 0; 251 } 252 253 static int omap2_apll_enable(struct clk_hw *hw) 254 { 255 struct clk_hw_omap *clk = to_clk_hw_omap(hw); 256 struct dpll_data *ad = clk->dpll_data; 257 u32 v; 258 int i = 0; 259 260 v = ti_clk_ll_ops->clk_readl(ad->control_reg); 261 v &= ~ad->enable_mask; 262 v |= OMAP2_EN_APLL_LOCKED << __ffs(ad->enable_mask); 263 ti_clk_ll_ops->clk_writel(v, ad->control_reg); 264 265 while (1) { 266 v = ti_clk_ll_ops->clk_readl(ad->idlest_reg); 267 if (v & ad->idlest_mask) 268 break; 269 if (i > MAX_APLL_WAIT_TRIES) 270 break; 271 i++; 272 udelay(1); 273 } 274 275 if (i == MAX_APLL_WAIT_TRIES) { 276 pr_warn("%s failed to transition to locked\n", 277 __clk_get_name(clk->hw.clk)); 278 return -EBUSY; 279 } 280 281 return 0; 282 } 283 284 static void omap2_apll_disable(struct clk_hw *hw) 285 { 286 struct clk_hw_omap *clk = to_clk_hw_omap(hw); 287 struct dpll_data *ad = clk->dpll_data; 288 u32 v; 289 290 v = ti_clk_ll_ops->clk_readl(ad->control_reg); 291 v &= ~ad->enable_mask; 292 v |= OMAP2_EN_APLL_STOPPED << __ffs(ad->enable_mask); 293 ti_clk_ll_ops->clk_writel(v, ad->control_reg); 294 } 295 296 static struct clk_ops omap2_apll_ops = { 297 .enable = &omap2_apll_enable, 298 .disable = &omap2_apll_disable, 299 .is_enabled = &omap2_apll_is_enabled, 300 .recalc_rate = &omap2_apll_recalc, 301 }; 302 303 static void omap2_apll_set_autoidle(struct clk_hw_omap *clk, u32 val) 304 { 305 struct dpll_data *ad = clk->dpll_data; 306 u32 v; 307 308 v = ti_clk_ll_ops->clk_readl(ad->autoidle_reg); 309 v &= ~ad->autoidle_mask; 310 v |= val << __ffs(ad->autoidle_mask); 311 ti_clk_ll_ops->clk_writel(v, ad->control_reg); 312 } 313 314 #define OMAP2_APLL_AUTOIDLE_LOW_POWER_STOP 0x3 315 #define OMAP2_APLL_AUTOIDLE_DISABLE 0x0 316 317 static void omap2_apll_allow_idle(struct clk_hw_omap *clk) 318 { 319 omap2_apll_set_autoidle(clk, OMAP2_APLL_AUTOIDLE_LOW_POWER_STOP); 320 } 321 322 static void omap2_apll_deny_idle(struct clk_hw_omap *clk) 323 { 324 omap2_apll_set_autoidle(clk, OMAP2_APLL_AUTOIDLE_DISABLE); 325 } 326 327 static struct clk_hw_omap_ops omap2_apll_hwops = { 328 .allow_idle = &omap2_apll_allow_idle, 329 .deny_idle = &omap2_apll_deny_idle, 330 }; 331 332 static void __init of_omap2_apll_setup(struct device_node *node) 333 { 334 struct dpll_data *ad = NULL; 335 struct clk_hw_omap *clk_hw = NULL; 336 struct clk_init_data *init = NULL; 337 struct clk *clk; 338 const char *parent_name; 339 u32 val; 340 341 ad = kzalloc(sizeof(*clk_hw), GFP_KERNEL); 342 clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL); 343 init = kzalloc(sizeof(*init), GFP_KERNEL); 344 345 if (!ad || !clk_hw || !init) 346 goto cleanup; 347 348 clk_hw->dpll_data = ad; 349 clk_hw->hw.init = init; 350 init->ops = &omap2_apll_ops; 351 init->name = node->name; 352 clk_hw->ops = &omap2_apll_hwops; 353 354 init->num_parents = of_clk_get_parent_count(node); 355 if (init->num_parents != 1) { 356 pr_err("%s must have one parent\n", node->name); 357 goto cleanup; 358 } 359 360 parent_name = of_clk_get_parent_name(node, 0); 361 init->parent_names = &parent_name; 362 363 if (of_property_read_u32(node, "ti,clock-frequency", &val)) { 364 pr_err("%s missing clock-frequency\n", node->name); 365 goto cleanup; 366 } 367 clk_hw->fixed_rate = val; 368 369 if (of_property_read_u32(node, "ti,bit-shift", &val)) { 370 pr_err("%s missing bit-shift\n", node->name); 371 goto cleanup; 372 } 373 374 clk_hw->enable_bit = val; 375 ad->enable_mask = 0x3 << val; 376 ad->autoidle_mask = 0x3 << val; 377 378 if (of_property_read_u32(node, "ti,idlest-shift", &val)) { 379 pr_err("%s missing idlest-shift\n", node->name); 380 goto cleanup; 381 } 382 383 ad->idlest_mask = 1 << val; 384 385 ad->control_reg = ti_clk_get_reg_addr(node, 0); 386 ad->autoidle_reg = ti_clk_get_reg_addr(node, 1); 387 ad->idlest_reg = ti_clk_get_reg_addr(node, 2); 388 389 if (!ad->control_reg || !ad->autoidle_reg || !ad->idlest_reg) 390 goto cleanup; 391 392 clk = clk_register(NULL, &clk_hw->hw); 393 if (!IS_ERR(clk)) { 394 of_clk_add_provider(node, of_clk_src_simple_get, clk); 395 kfree(init); 396 return; 397 } 398 cleanup: 399 kfree(ad); 400 kfree(clk_hw); 401 kfree(init); 402 } 403 CLK_OF_DECLARE(omap2_apll_clock, "ti,omap2-apll-clock", 404 of_omap2_apll_setup); 405