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