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