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; 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 157 /* register a list of fixed factor clocks */ 158 void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx, 159 const struct samsung_fixed_factor_clock *list, unsigned int nr_clk) 160 { 161 struct clk_hw *clk_hw; 162 unsigned int idx; 163 164 for (idx = 0; idx < nr_clk; idx++, list++) { 165 clk_hw = clk_hw_register_fixed_factor(ctx->dev, list->name, 166 list->parent_name, list->flags, list->mult, list->div); 167 if (IS_ERR(clk_hw)) { 168 pr_err("%s: failed to register clock %s\n", __func__, 169 list->name); 170 continue; 171 } 172 173 samsung_clk_add_lookup(ctx, clk_hw, list->id); 174 } 175 } 176 177 /* register a list of mux clocks */ 178 void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx, 179 const struct samsung_mux_clock *list, 180 unsigned int nr_clk) 181 { 182 struct clk_hw *clk_hw; 183 unsigned int idx; 184 185 for (idx = 0; idx < nr_clk; idx++, list++) { 186 clk_hw = clk_hw_register_mux(ctx->dev, list->name, 187 list->parent_names, list->num_parents, list->flags, 188 ctx->reg_base + list->offset, 189 list->shift, list->width, list->mux_flags, &ctx->lock); 190 if (IS_ERR(clk_hw)) { 191 pr_err("%s: failed to register clock %s\n", __func__, 192 list->name); 193 continue; 194 } 195 196 samsung_clk_add_lookup(ctx, clk_hw, list->id); 197 } 198 } 199 200 /* register a list of div clocks */ 201 void __init samsung_clk_register_div(struct samsung_clk_provider *ctx, 202 const struct samsung_div_clock *list, 203 unsigned int nr_clk) 204 { 205 struct clk_hw *clk_hw; 206 unsigned int idx; 207 208 for (idx = 0; idx < nr_clk; idx++, list++) { 209 if (list->table) 210 clk_hw = clk_hw_register_divider_table(ctx->dev, 211 list->name, list->parent_name, list->flags, 212 ctx->reg_base + list->offset, 213 list->shift, list->width, list->div_flags, 214 list->table, &ctx->lock); 215 else 216 clk_hw = clk_hw_register_divider(ctx->dev, list->name, 217 list->parent_name, list->flags, 218 ctx->reg_base + list->offset, list->shift, 219 list->width, list->div_flags, &ctx->lock); 220 if (IS_ERR(clk_hw)) { 221 pr_err("%s: failed to register clock %s\n", __func__, 222 list->name); 223 continue; 224 } 225 226 samsung_clk_add_lookup(ctx, clk_hw, list->id); 227 } 228 } 229 230 /* register a list of gate clocks */ 231 void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx, 232 const struct samsung_gate_clock *list, 233 unsigned int nr_clk) 234 { 235 struct clk_hw *clk_hw; 236 unsigned int idx; 237 238 for (idx = 0; idx < nr_clk; idx++, list++) { 239 clk_hw = clk_hw_register_gate(ctx->dev, list->name, list->parent_name, 240 list->flags, ctx->reg_base + list->offset, 241 list->bit_idx, list->gate_flags, &ctx->lock); 242 if (IS_ERR(clk_hw)) { 243 pr_err("%s: failed to register clock %s\n", __func__, 244 list->name); 245 continue; 246 } 247 248 samsung_clk_add_lookup(ctx, clk_hw, list->id); 249 } 250 } 251 252 /* 253 * obtain the clock speed of all external fixed clock sources from device 254 * tree and register it 255 */ 256 void __init samsung_clk_of_register_fixed_ext(struct samsung_clk_provider *ctx, 257 struct samsung_fixed_rate_clock *fixed_rate_clk, 258 unsigned int nr_fixed_rate_clk, 259 const struct of_device_id *clk_matches) 260 { 261 const struct of_device_id *match; 262 struct device_node *clk_np; 263 u32 freq; 264 265 for_each_matching_node_and_match(clk_np, clk_matches, &match) { 266 if (of_property_read_u32(clk_np, "clock-frequency", &freq)) 267 continue; 268 fixed_rate_clk[(unsigned long)match->data].fixed_rate = freq; 269 } 270 samsung_clk_register_fixed_rate(ctx, fixed_rate_clk, nr_fixed_rate_clk); 271 } 272 273 #ifdef CONFIG_PM_SLEEP 274 static int samsung_clk_suspend(void) 275 { 276 struct samsung_clock_reg_cache *reg_cache; 277 278 list_for_each_entry(reg_cache, &clock_reg_cache_list, node) { 279 samsung_clk_save(reg_cache->reg_base, reg_cache->rdump, 280 reg_cache->rd_num); 281 samsung_clk_restore(reg_cache->reg_base, reg_cache->rsuspend, 282 reg_cache->rsuspend_num); 283 } 284 return 0; 285 } 286 287 static void samsung_clk_resume(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_restore(reg_cache->reg_base, reg_cache->rdump, 293 reg_cache->rd_num); 294 } 295 296 static struct syscore_ops samsung_clk_syscore_ops = { 297 .suspend = samsung_clk_suspend, 298 .resume = samsung_clk_resume, 299 }; 300 301 void samsung_clk_extended_sleep_init(void __iomem *reg_base, 302 const unsigned long *rdump, 303 unsigned long nr_rdump, 304 const struct samsung_clk_reg_dump *rsuspend, 305 unsigned long nr_rsuspend) 306 { 307 struct samsung_clock_reg_cache *reg_cache; 308 309 reg_cache = kzalloc(sizeof(struct samsung_clock_reg_cache), 310 GFP_KERNEL); 311 if (!reg_cache) 312 panic("could not allocate register reg_cache.\n"); 313 reg_cache->rdump = samsung_clk_alloc_reg_dump(rdump, nr_rdump); 314 315 if (!reg_cache->rdump) 316 panic("could not allocate register dump storage.\n"); 317 318 if (list_empty(&clock_reg_cache_list)) 319 register_syscore_ops(&samsung_clk_syscore_ops); 320 321 reg_cache->reg_base = reg_base; 322 reg_cache->rd_num = nr_rdump; 323 reg_cache->rsuspend = rsuspend; 324 reg_cache->rsuspend_num = nr_rsuspend; 325 list_add_tail(®_cache->node, &clock_reg_cache_list); 326 } 327 #endif 328 329 /** 330 * samsung_cmu_register_clocks() - Register all clocks provided in CMU object 331 * @ctx: Clock provider object 332 * @cmu: CMU object with clocks to register 333 */ 334 void __init samsung_cmu_register_clocks(struct samsung_clk_provider *ctx, 335 const struct samsung_cmu_info *cmu) 336 { 337 if (cmu->pll_clks) 338 samsung_clk_register_pll(ctx, cmu->pll_clks, cmu->nr_pll_clks); 339 if (cmu->mux_clks) 340 samsung_clk_register_mux(ctx, cmu->mux_clks, cmu->nr_mux_clks); 341 if (cmu->div_clks) 342 samsung_clk_register_div(ctx, cmu->div_clks, cmu->nr_div_clks); 343 if (cmu->gate_clks) 344 samsung_clk_register_gate(ctx, cmu->gate_clks, 345 cmu->nr_gate_clks); 346 if (cmu->fixed_clks) 347 samsung_clk_register_fixed_rate(ctx, cmu->fixed_clks, 348 cmu->nr_fixed_clks); 349 if (cmu->fixed_factor_clks) 350 samsung_clk_register_fixed_factor(ctx, cmu->fixed_factor_clks, 351 cmu->nr_fixed_factor_clks); 352 if (cmu->cpu_clks) 353 samsung_clk_register_cpu(ctx, cmu->cpu_clks, cmu->nr_cpu_clks); 354 } 355 356 /* 357 * Common function which registers plls, muxes, dividers and gates 358 * for each CMU. It also add CMU register list to register cache. 359 */ 360 struct samsung_clk_provider * __init samsung_cmu_register_one( 361 struct device_node *np, 362 const struct samsung_cmu_info *cmu) 363 { 364 void __iomem *reg_base; 365 struct samsung_clk_provider *ctx; 366 367 reg_base = of_iomap(np, 0); 368 if (!reg_base) { 369 panic("%s: failed to map registers\n", __func__); 370 return NULL; 371 } 372 373 ctx = samsung_clk_init(NULL, reg_base, cmu->nr_clk_ids); 374 samsung_cmu_register_clocks(ctx, cmu); 375 376 if (cmu->clk_regs) 377 samsung_clk_extended_sleep_init(reg_base, 378 cmu->clk_regs, cmu->nr_clk_regs, 379 cmu->suspend_regs, cmu->nr_suspend_regs); 380 381 samsung_clk_of_add_provider(np, ctx); 382 383 return ctx; 384 } 385