1 /* 2 * Copyright (c) 2013 Samsung Electronics Co., Ltd. 3 * Copyright (c) 2013 Linaro Ltd. 4 * Author: Thomas Abraham <thomas.ab@samsung.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 * 10 * This file includes utility functions to register clocks to common 11 * clock framework for Samsung platforms. 12 */ 13 14 #include <linux/slab.h> 15 #include <linux/clkdev.h> 16 #include <linux/clk.h> 17 #include <linux/clk-provider.h> 18 #include <linux/of_address.h> 19 #include <linux/syscore_ops.h> 20 21 #include "clk.h" 22 23 static LIST_HEAD(clock_reg_cache_list); 24 25 void samsung_clk_save(void __iomem *base, 26 struct samsung_clk_reg_dump *rd, 27 unsigned int num_regs) 28 { 29 for (; num_regs > 0; --num_regs, ++rd) 30 rd->value = readl(base + rd->offset); 31 } 32 33 void samsung_clk_restore(void __iomem *base, 34 const struct samsung_clk_reg_dump *rd, 35 unsigned int num_regs) 36 { 37 for (; num_regs > 0; --num_regs, ++rd) 38 writel(rd->value, base + rd->offset); 39 } 40 41 struct samsung_clk_reg_dump *samsung_clk_alloc_reg_dump( 42 const unsigned long *rdump, 43 unsigned long nr_rdump) 44 { 45 struct samsung_clk_reg_dump *rd; 46 unsigned int i; 47 48 rd = kcalloc(nr_rdump, sizeof(*rd), GFP_KERNEL); 49 if (!rd) 50 return NULL; 51 52 for (i = 0; i < nr_rdump; ++i) 53 rd[i].offset = rdump[i]; 54 55 return rd; 56 } 57 58 /* setup the essentials required to support clock lookup using ccf */ 59 struct samsung_clk_provider *__init samsung_clk_init(struct device_node *np, 60 void __iomem *base, unsigned long nr_clks) 61 { 62 struct samsung_clk_provider *ctx; 63 struct clk **clk_table; 64 int i; 65 66 ctx = kzalloc(sizeof(struct samsung_clk_provider), GFP_KERNEL); 67 if (!ctx) 68 panic("could not allocate clock provider context.\n"); 69 70 clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL); 71 if (!clk_table) 72 panic("could not allocate clock lookup table\n"); 73 74 for (i = 0; i < nr_clks; ++i) 75 clk_table[i] = ERR_PTR(-ENOENT); 76 77 ctx->reg_base = base; 78 ctx->clk_data.clks = clk_table; 79 ctx->clk_data.clk_num = nr_clks; 80 spin_lock_init(&ctx->lock); 81 82 return ctx; 83 } 84 85 void __init samsung_clk_of_add_provider(struct device_node *np, 86 struct samsung_clk_provider *ctx) 87 { 88 if (np) { 89 if (of_clk_add_provider(np, of_clk_src_onecell_get, 90 &ctx->clk_data)) 91 panic("could not register clk provider\n"); 92 } 93 } 94 95 /* add a clock instance to the clock lookup table used for dt based lookup */ 96 void samsung_clk_add_lookup(struct samsung_clk_provider *ctx, struct clk *clk, 97 unsigned int id) 98 { 99 if (ctx->clk_data.clks && id) 100 ctx->clk_data.clks[id] = clk; 101 } 102 103 /* register a list of aliases */ 104 void __init samsung_clk_register_alias(struct samsung_clk_provider *ctx, 105 const struct samsung_clock_alias *list, 106 unsigned int nr_clk) 107 { 108 struct clk *clk; 109 unsigned int idx, ret; 110 111 if (!ctx->clk_data.clks) { 112 pr_err("%s: clock table missing\n", __func__); 113 return; 114 } 115 116 for (idx = 0; idx < nr_clk; idx++, list++) { 117 if (!list->id) { 118 pr_err("%s: clock id missing for index %d\n", __func__, 119 idx); 120 continue; 121 } 122 123 clk = ctx->clk_data.clks[list->id]; 124 if (!clk) { 125 pr_err("%s: failed to find clock %d\n", __func__, 126 list->id); 127 continue; 128 } 129 130 ret = clk_register_clkdev(clk, list->alias, list->dev_name); 131 if (ret) 132 pr_err("%s: failed to register lookup %s\n", 133 __func__, list->alias); 134 } 135 } 136 137 /* register a list of fixed clocks */ 138 void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx, 139 const struct samsung_fixed_rate_clock *list, 140 unsigned int nr_clk) 141 { 142 struct clk *clk; 143 unsigned int idx, ret; 144 145 for (idx = 0; idx < nr_clk; idx++, list++) { 146 clk = clk_register_fixed_rate(NULL, list->name, 147 list->parent_name, list->flags, list->fixed_rate); 148 if (IS_ERR(clk)) { 149 pr_err("%s: failed to register clock %s\n", __func__, 150 list->name); 151 continue; 152 } 153 154 samsung_clk_add_lookup(ctx, clk, list->id); 155 156 /* 157 * Unconditionally add a clock lookup for the fixed rate clocks. 158 * There are not many of these on any of Samsung platforms. 159 */ 160 ret = clk_register_clkdev(clk, list->name, NULL); 161 if (ret) 162 pr_err("%s: failed to register clock lookup for %s", 163 __func__, list->name); 164 } 165 } 166 167 /* register a list of fixed factor clocks */ 168 void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx, 169 const struct samsung_fixed_factor_clock *list, unsigned int nr_clk) 170 { 171 struct clk *clk; 172 unsigned int idx; 173 174 for (idx = 0; idx < nr_clk; idx++, list++) { 175 clk = clk_register_fixed_factor(NULL, list->name, 176 list->parent_name, list->flags, list->mult, list->div); 177 if (IS_ERR(clk)) { 178 pr_err("%s: failed to register clock %s\n", __func__, 179 list->name); 180 continue; 181 } 182 183 samsung_clk_add_lookup(ctx, clk, list->id); 184 } 185 } 186 187 /* register a list of mux clocks */ 188 void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx, 189 const struct samsung_mux_clock *list, 190 unsigned int nr_clk) 191 { 192 struct clk *clk; 193 unsigned int idx, ret; 194 195 for (idx = 0; idx < nr_clk; idx++, list++) { 196 clk = clk_register_mux(NULL, list->name, list->parent_names, 197 list->num_parents, list->flags, 198 ctx->reg_base + list->offset, 199 list->shift, list->width, list->mux_flags, &ctx->lock); 200 if (IS_ERR(clk)) { 201 pr_err("%s: failed to register clock %s\n", __func__, 202 list->name); 203 continue; 204 } 205 206 samsung_clk_add_lookup(ctx, clk, list->id); 207 208 /* register a clock lookup only if a clock alias is specified */ 209 if (list->alias) { 210 ret = clk_register_clkdev(clk, list->alias, 211 list->dev_name); 212 if (ret) 213 pr_err("%s: failed to register lookup %s\n", 214 __func__, list->alias); 215 } 216 } 217 } 218 219 /* register a list of div clocks */ 220 void __init samsung_clk_register_div(struct samsung_clk_provider *ctx, 221 const struct samsung_div_clock *list, 222 unsigned int nr_clk) 223 { 224 struct clk *clk; 225 unsigned int idx, ret; 226 227 for (idx = 0; idx < nr_clk; idx++, list++) { 228 if (list->table) 229 clk = clk_register_divider_table(NULL, list->name, 230 list->parent_name, list->flags, 231 ctx->reg_base + list->offset, 232 list->shift, list->width, list->div_flags, 233 list->table, &ctx->lock); 234 else 235 clk = clk_register_divider(NULL, list->name, 236 list->parent_name, list->flags, 237 ctx->reg_base + list->offset, list->shift, 238 list->width, list->div_flags, &ctx->lock); 239 if (IS_ERR(clk)) { 240 pr_err("%s: failed to register clock %s\n", __func__, 241 list->name); 242 continue; 243 } 244 245 samsung_clk_add_lookup(ctx, clk, list->id); 246 247 /* register a clock lookup only if a clock alias is specified */ 248 if (list->alias) { 249 ret = clk_register_clkdev(clk, list->alias, 250 list->dev_name); 251 if (ret) 252 pr_err("%s: failed to register lookup %s\n", 253 __func__, list->alias); 254 } 255 } 256 } 257 258 /* register a list of gate clocks */ 259 void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx, 260 const struct samsung_gate_clock *list, 261 unsigned int nr_clk) 262 { 263 struct clk *clk; 264 unsigned int idx, ret; 265 266 for (idx = 0; idx < nr_clk; idx++, list++) { 267 clk = clk_register_gate(NULL, list->name, list->parent_name, 268 list->flags, ctx->reg_base + list->offset, 269 list->bit_idx, list->gate_flags, &ctx->lock); 270 if (IS_ERR(clk)) { 271 pr_err("%s: failed to register clock %s\n", __func__, 272 list->name); 273 continue; 274 } 275 276 /* register a clock lookup only if a clock alias is specified */ 277 if (list->alias) { 278 ret = clk_register_clkdev(clk, list->alias, 279 list->dev_name); 280 if (ret) 281 pr_err("%s: failed to register lookup %s\n", 282 __func__, list->alias); 283 } 284 285 samsung_clk_add_lookup(ctx, clk, list->id); 286 } 287 } 288 289 /* 290 * obtain the clock speed of all external fixed clock sources from device 291 * tree and register it 292 */ 293 void __init samsung_clk_of_register_fixed_ext(struct samsung_clk_provider *ctx, 294 struct samsung_fixed_rate_clock *fixed_rate_clk, 295 unsigned int nr_fixed_rate_clk, 296 const struct of_device_id *clk_matches) 297 { 298 const struct of_device_id *match; 299 struct device_node *clk_np; 300 u32 freq; 301 302 for_each_matching_node_and_match(clk_np, clk_matches, &match) { 303 if (of_property_read_u32(clk_np, "clock-frequency", &freq)) 304 continue; 305 fixed_rate_clk[(unsigned long)match->data].fixed_rate = freq; 306 } 307 samsung_clk_register_fixed_rate(ctx, fixed_rate_clk, nr_fixed_rate_clk); 308 } 309 310 /* utility function to get the rate of a specified clock */ 311 unsigned long _get_rate(const char *clk_name) 312 { 313 struct clk *clk; 314 315 clk = __clk_lookup(clk_name); 316 if (!clk) { 317 pr_err("%s: could not find clock %s\n", __func__, clk_name); 318 return 0; 319 } 320 321 return clk_get_rate(clk); 322 } 323 324 #ifdef CONFIG_PM_SLEEP 325 static int samsung_clk_suspend(void) 326 { 327 struct samsung_clock_reg_cache *reg_cache; 328 329 list_for_each_entry(reg_cache, &clock_reg_cache_list, node) 330 samsung_clk_save(reg_cache->reg_base, reg_cache->rdump, 331 reg_cache->rd_num); 332 return 0; 333 } 334 335 static void samsung_clk_resume(void) 336 { 337 struct samsung_clock_reg_cache *reg_cache; 338 339 list_for_each_entry(reg_cache, &clock_reg_cache_list, node) 340 samsung_clk_restore(reg_cache->reg_base, reg_cache->rdump, 341 reg_cache->rd_num); 342 } 343 344 static struct syscore_ops samsung_clk_syscore_ops = { 345 .suspend = samsung_clk_suspend, 346 .resume = samsung_clk_resume, 347 }; 348 349 void samsung_clk_sleep_init(void __iomem *reg_base, 350 const unsigned long *rdump, 351 unsigned long nr_rdump) 352 { 353 struct samsung_clock_reg_cache *reg_cache; 354 355 reg_cache = kzalloc(sizeof(struct samsung_clock_reg_cache), 356 GFP_KERNEL); 357 if (!reg_cache) 358 panic("could not allocate register reg_cache.\n"); 359 reg_cache->rdump = samsung_clk_alloc_reg_dump(rdump, nr_rdump); 360 361 if (!reg_cache->rdump) 362 panic("could not allocate register dump storage.\n"); 363 364 if (list_empty(&clock_reg_cache_list)) 365 register_syscore_ops(&samsung_clk_syscore_ops); 366 367 reg_cache->reg_base = reg_base; 368 reg_cache->rd_num = nr_rdump; 369 list_add_tail(®_cache->node, &clock_reg_cache_list); 370 } 371 372 #else 373 void samsung_clk_sleep_init(void __iomem *reg_base, 374 const unsigned long *rdump, 375 unsigned long nr_rdump) {} 376 #endif 377 378 /* 379 * Common function which registers plls, muxes, dividers and gates 380 * for each CMU. It also add CMU register list to register cache. 381 */ 382 struct samsung_clk_provider * __init samsung_cmu_register_one( 383 struct device_node *np, 384 const struct samsung_cmu_info *cmu) 385 { 386 void __iomem *reg_base; 387 struct samsung_clk_provider *ctx; 388 389 reg_base = of_iomap(np, 0); 390 if (!reg_base) { 391 panic("%s: failed to map registers\n", __func__); 392 return NULL; 393 } 394 395 ctx = samsung_clk_init(np, reg_base, cmu->nr_clk_ids); 396 if (!ctx) { 397 panic("%s: unable to allocate ctx\n", __func__); 398 return ctx; 399 } 400 401 if (cmu->pll_clks) 402 samsung_clk_register_pll(ctx, cmu->pll_clks, cmu->nr_pll_clks, 403 reg_base); 404 if (cmu->mux_clks) 405 samsung_clk_register_mux(ctx, cmu->mux_clks, 406 cmu->nr_mux_clks); 407 if (cmu->div_clks) 408 samsung_clk_register_div(ctx, cmu->div_clks, cmu->nr_div_clks); 409 if (cmu->gate_clks) 410 samsung_clk_register_gate(ctx, cmu->gate_clks, 411 cmu->nr_gate_clks); 412 if (cmu->fixed_clks) 413 samsung_clk_register_fixed_rate(ctx, cmu->fixed_clks, 414 cmu->nr_fixed_clks); 415 if (cmu->fixed_factor_clks) 416 samsung_clk_register_fixed_factor(ctx, cmu->fixed_factor_clks, 417 cmu->nr_fixed_factor_clks); 418 if (cmu->clk_regs) 419 samsung_clk_sleep_init(reg_base, cmu->clk_regs, 420 cmu->nr_clk_regs); 421 422 samsung_clk_of_add_provider(np, ctx); 423 424 return ctx; 425 } 426