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