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